diff options
468 files changed, 17103 insertions, 6715 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index ee31c505f..fc93cc505 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,4 +5,3 @@ commit_msg_changeid_field = true [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES} -strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT} diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp index 5a4d2930e..cb2e287ac 100644 --- a/PermissionController/Android.bp +++ b/PermissionController/Android.bp @@ -45,7 +45,10 @@ genrule { java_library { name: "permissioncontroller-statsd", sdk_version: "system_current", - + min_sdk_version: "30", + apex_available: [ + "com.android.permission", + ], srcs: [ ":statslog-permissioncontroller-java-gen", ], @@ -136,10 +139,13 @@ android_app { "SettingsLibUtils", "modules-utils-build_system", "safety-center-resources-lib", + "lottie", + "safety-label" ], proto: { type: "lite", + include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"], }, lint: { @@ -150,7 +156,10 @@ android_app { proguard_flags_files: ["proguard.flags"], }, - plugins: ["java_api_finder"], - kotlincflags: ["-Xjvm-default=enable"], + + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], } diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml index 83d598888..004d87064 100644 --- a/PermissionController/AndroidManifest.xml +++ b/PermissionController/AndroidManifest.xml @@ -64,6 +64,7 @@ <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> <uses-permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"/> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" /> <application android:name="com.android.permissioncontroller.PermissionControllerApplication" android:label="@string/app_name" @@ -312,7 +313,7 @@ <activity android:name="com.android.permissioncontroller.permission.ui.OverlayWarningDialog" android:excludeFromRecents="true" android:exported="false" - android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert" /> + android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight" /> <activity android:name="com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog" android:excludeFromRecents="true" @@ -523,9 +524,10 @@ </receiver> <activity android:name="com.android.permissioncontroller.incident.ConfirmationActivity" - android:theme="@style/Theme.DeviceDefault.Dialog.Alert.DayNight" + android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight" android:exported="false" android:excludeFromRecents="true" + android:finishOnCloseSystemDialogs="true" android:noHistory="true" /> <receiver android:name="com.android.permissioncontroller.incident.ApprovalReceiver" diff --git a/PermissionController/TEST_MAPPING b/PermissionController/TEST_MAPPING index 0911e0d91..0ae3818fd 100644 --- a/PermissionController/TEST_MAPPING +++ b/PermissionController/TEST_MAPPING @@ -11,6 +11,74 @@ } ], "file_patterns": ["res/xml/roles\\.xml"] + }, + { + "name": "PermissionUiTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ], + "presubmit-large": [ + { + "name": "CtsPermission3TestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ], + "mainline-presubmit": [ + { + "name": "CtsRoleTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677748): These two tests currently fails on R base image + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList" + }, + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ], + "file_patterns": ["res/xml/roles\\.xml"] + }, + { + "name": "PermissionUiTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + // TODO(b/238773220): These tests currently fails on R base image + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsGranted" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsRevoked" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenAppGetsInstalled" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstalled" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenUserGetsUninstalled" + } + ] + }, + { + "name": "CtsPermission3TestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] } ], "imports": [ diff --git a/PermissionController/iconloaderlib/build.gradle b/PermissionController/iconloaderlib/build.gradle index 84102758e..35ca8dee2 100644 --- a/PermissionController/iconloaderlib/build.gradle +++ b/PermissionController/iconloaderlib/build.gradle @@ -34,5 +34,5 @@ android { } dependencies { - implementation "androidx.core:core:${ANDROID_X_VERSION}" + implementation "androidx.core:core" } diff --git a/PermissionController/proguard.flags b/PermissionController/proguard.flags index 1f0b03269..13590aa39 100644 --- a/PermissionController/proguard.flags +++ b/PermissionController/proguard.flags @@ -8,7 +8,12 @@ -dontwarn androidx.core.** # Keep classes that implements RoleBehavior, which are used by reflection. --keep class * implements com.android.permissioncontroller.role.model.RoleBehavior { +-keep class * implements com.android.role.controller.model.RoleBehavior { + *; +} + +# Keep classes that implements RoleUiBehavior, which are used by reflection. +-keep class * implements com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior { *; } @@ -25,4 +30,4 @@ *** get*(); *** set*(***); *** has*(); -}
\ No newline at end of file +} diff --git a/PermissionController/res/drawable/coarse_off_dark.gif b/PermissionController/res/drawable/coarse_off_dark.gif Binary files differdeleted file mode 100644 index 09a7da18d..000000000 --- a/PermissionController/res/drawable/coarse_off_dark.gif +++ /dev/null diff --git a/PermissionController/res/drawable/coarse_off_light.gif b/PermissionController/res/drawable/coarse_off_light.gif Binary files differdeleted file mode 100644 index a5419cd91..000000000 --- a/PermissionController/res/drawable/coarse_off_light.gif +++ /dev/null diff --git a/PermissionController/res/drawable/coarse_on_dark.gif b/PermissionController/res/drawable/coarse_on_dark.gif Binary files differdeleted file mode 100644 index a2ea07bd0..000000000 --- a/PermissionController/res/drawable/coarse_on_dark.gif +++ /dev/null diff --git a/PermissionController/res/drawable/coarse_on_light.gif b/PermissionController/res/drawable/coarse_on_light.gif Binary files differdeleted file mode 100644 index 491edb612..000000000 --- a/PermissionController/res/drawable/coarse_on_light.gif +++ /dev/null diff --git a/PermissionController/res/drawable/fine_off_dark.gif b/PermissionController/res/drawable/fine_off_dark.gif Binary files differdeleted file mode 100644 index 560e38a34..000000000 --- a/PermissionController/res/drawable/fine_off_dark.gif +++ /dev/null diff --git a/PermissionController/res/drawable/fine_off_light.gif b/PermissionController/res/drawable/fine_off_light.gif Binary files differdeleted file mode 100644 index 5661b9270..000000000 --- a/PermissionController/res/drawable/fine_off_light.gif +++ /dev/null diff --git a/PermissionController/res/drawable/fine_on_dark.gif b/PermissionController/res/drawable/fine_on_dark.gif Binary files differdeleted file mode 100644 index aadf7821b..000000000 --- a/PermissionController/res/drawable/fine_on_dark.gif +++ /dev/null diff --git a/PermissionController/res/drawable/fine_on_light.gif b/PermissionController/res/drawable/fine_on_light.gif Binary files differdeleted file mode 100644 index a592c6305..000000000 --- a/PermissionController/res/drawable/fine_on_light.gif +++ /dev/null diff --git a/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml new file mode 100644 index 000000000..b76b68c5e --- /dev/null +++ b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + <corners android:radius="16dp"/> + <stroke android:width="1dp" android:color="?androidprv:attr/textColorSecondaryInverse" /> +</shape>
\ No newline at end of file diff --git a/PermissionController/res/drawable/ic_more_info_arrow.xml b/PermissionController/res/drawable/ic_more_info_arrow.xml new file mode 100644 index 000000000..73eb5ccfc --- /dev/null +++ b/PermissionController/res/drawable/ic_more_info_arrow.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" + android:height="48dp" android:viewportWidth="48" android:viewportHeight="48" + android:autoMirrored="true" android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/white" + android:pathData="M18.75,36 L16.6,33.85 26.5,23.95 16.6,14.05 18.75,11.9 30.8,23.95Z"/> +</vector>
\ No newline at end of file diff --git a/PermissionController/res/drawable/ic_shield_exclamation_outline.xml b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml new file mode 100644 index 000000000..5785babf9 --- /dev/null +++ b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" + android:height="48dp" android:viewportWidth="48" android:viewportHeight="48" + android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/white" + android:pathData="M24,31.3Q24.7,31.3 25.2,30.8Q25.7,30.3 25.7,29.6Q25.7,28.9 25.2,28.4Q24.7,27.9 24,27.9Q23.3,27.9 22.8,28.4Q22.3,28.9 22.3,29.6Q22.3,30.3 22.8,30.8Q23.3,31.3 24,31.3ZM22.5,24.6H25.5V14.25H22.5ZM24,43.95Q17,42.2 12.5,35.825Q8,29.45 8,21.85V9.95L24,3.95L40,9.95V21.85Q40,29.45 35.5,35.825Q31,42.2 24,43.95ZM24,40.85Q29.75,38.95 33.375,33.675Q37,28.4 37,21.85V12.05L24,7.15L11,12.05V21.85Q11,28.4 14.625,33.675Q18.25,38.95 24,40.85ZM24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Z"/> +</vector> diff --git a/PermissionController/res/layout-television/preference_permissions_category.xml b/PermissionController/res/layout-television/preference_permissions_category.xml index 59412065d..ff5cc6147 100644 --- a/PermissionController/res/layout-television/preference_permissions_category.xml +++ b/PermissionController/res/layout-television/preference_permissions_category.xml @@ -25,7 +25,7 @@ android:paddingTop="16dp" android:paddingBottom="16dp"> <TextView - android:id="@+android:id/title" + android:id="@android:id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/settings_category_text_color" @@ -33,7 +33,7 @@ android:textStyle="bold"/> <TextView - android:id="@+android:id/summary" + android:id="@android:id/summary" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" diff --git a/PermissionController/res/layout-television/preference_permissions_no_apps.xml b/PermissionController/res/layout-television/preference_permissions_no_apps.xml index dbcd3ec25..0511ecf57 100644 --- a/PermissionController/res/layout-television/preference_permissions_no_apps.xml +++ b/PermissionController/res/layout-television/preference_permissions_no_apps.xml @@ -17,7 +17,7 @@ <!-- Layout used for PreferenceCategory in a PreferenceActivity. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+android:id/title" + android:id="@android:id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" diff --git a/PermissionController/res/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml index 318ae015e..36c04be94 100644 --- a/PermissionController/res/layout-v33/preference_issue_card.xml +++ b/PermissionController/res/layout-v33/preference_issue_card.xml @@ -28,6 +28,12 @@ style="@style/SafetyCenterIssueDismiss" /> <TextView + android:id="@+id/issue_card_attribution_title" + android:text="@string/summary_placeholder" + android:importantForAccessibility="no" + style="@style/SafetyCenterIssueAttributionTitle" /> + + <TextView android:id="@+id/issue_card_title" android:text="@string/summary_placeholder" android:importantForAccessibility="no" diff --git a/PermissionController/res/layout/app_permission.xml b/PermissionController/res/layout/app_permission.xml index 697150b6f..3f9260740 100644 --- a/PermissionController/res/layout/app_permission.xml +++ b/PermissionController/res/layout/app_permission.xml @@ -64,6 +64,11 @@ style="@style/AppPermissionRadioButton" /> <RadioButton + android:id="@+id/select_photos_radio_button" + android:text="@string/app_permission_button_select_photos" + style="@style/AppPermissionRadioButton" /> + + <RadioButton android:id="@+id/ask_one_time_radio_button" android:text="@string/app_permission_button_ask" style="@style/AppPermissionRadioButton" /> diff --git a/PermissionController/res/layout/grant_permissions.xml b/PermissionController/res/layout/grant_permissions.xml index cda679d05..ca0706171 100644 --- a/PermissionController/res/layout/grant_permissions.xml +++ b/PermissionController/res/layout/grant_permissions.xml @@ -62,6 +62,30 @@ </LinearLayout> + <!-- permission rationale --> + <LinearLayout + android:id="@+id/permission_rationale_container" + style="@style/PermissionGrantPermissionRationaleContent"> + + <ImageView + android:id="@+id/permission_rationale_icon" + android:importantForAccessibility="no" + android:src="@drawable/ic_shield_exclamation_outline" + style="@style/PermissionGrantPermissionRationaleIcon" /> + + <TextView + android:id="@+id/permission_rationale_message" + style="@style/PermissionGrantPermissionRationaleMessage" /> + + <ImageView + android:id="@+id/permission_rationale_more_info_icon" + android:importantForAccessibility="no" + android:src="@drawable/ic_more_info_arrow" + style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" /> + + </LinearLayout> + + <!-- location (precise/approximate) animations --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -117,11 +141,31 @@ style="@style/PermissionGrantButtonAllowOneTime" /> <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_all_photos_button" + android:text="@string/grant_dialog_button_allow_all_photos" + style="@style/PermissionGrantButtonAllowAllPhotos" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_more_selected_photos_button" + android:text="@string/grant_dialog_button_allow_more_selected_photos" + style="@style/PermissionGrantButtonAllowMorePhotos" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_selected_photos_button" + android:text="@string/grant_dialog_button_allow_selected_photos" + style="@style/PermissionGrantButtonAllowSelectedPhotos" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton android:id="@+id/permission_deny_button" android:text="@string/grant_dialog_button_deny" style="@style/PermissionGrantButtonDeny" /> <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_dont_allow_more_selected_photos_button" + android:text="@string/grant_dialog_button_dont_allow_more_selected_photos" + style="@style/PermissionGrantButtonDeny" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton android:id="@+id/permission_deny_and_dont_ask_again_button" android:text="@string/grant_dialog_button_deny" style="@style/PermissionGrantButtonDeny" /> diff --git a/PermissionController/res/layout/grant_permissions_material3.xml b/PermissionController/res/layout/grant_permissions_material3.xml index 317a33b55..9c3ab6eef 100644 --- a/PermissionController/res/layout/grant_permissions_material3.xml +++ b/PermissionController/res/layout/grant_permissions_material3.xml @@ -63,6 +63,30 @@ </LinearLayout> + <!-- permission rationale --> + <LinearLayout + android:id="@+id/permission_rationale_container" + style="@style/PermissionGrantPermissionRationaleContent"> + + <ImageView + android:id="@+id/permission_rationale_icon" + android:importantForAccessibility="no" + android:src="@drawable/ic_shield_exclamation_outline" + style="@style/PermissionGrantPermissionRationaleIcon" /> + + <TextView + android:id="@+id/permission_rationale_message" + style="@style/PermissionGrantPermissionRationaleMessage" /> + + <ImageView + android:id="@+id/permission_rationale_more_info_icon" + android:importantForAccessibility="no" + android:src="@drawable/ic_more_info_arrow" + style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" /> + + </LinearLayout> + + <!-- location (precise/approximate) animations --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -70,7 +94,7 @@ <RadioGroup android:id="@+id/permission_location_accuracy_radio_group" - style="@style/PermissionLocationAccuracyRadioGroup"> + style="@style/PermissionLocationAccuracyRadioGroupMaterial3"> <RadioButton android:id="@+id/permission_location_accuracy_radio_fine" @@ -86,12 +110,12 @@ <ImageView android:id="@+id/permission_location_accuracy_fine_only" android:contentDescription="@string/precise_image_description" - style="@style/PermissionLocationAccuracyFineImageView" /> + style="@style/PermissionLocationAccuracyFineImageViewMaterial3" /> <ImageView android:id="@+id/permission_location_accuracy_coarse_only" android:contentDescription="@string/approximate_image_description" - style="@style/PermissionLocationAccuracyCoarseImageView" /> + style="@style/PermissionLocationAccuracyCoarseImageViewMaterial3" /> </LinearLayout> @@ -118,11 +142,31 @@ style="@style/PermissionGrantButtonAllowOneTimeMaterial3" /> <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_all_photos_button" + android:text="@string/grant_dialog_button_allow_all_photos" + style="@style/PermissionGrantButtonAllowAllPhotosMaterial3" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_more_selected_photos_button" + android:text="@string/grant_dialog_button_allow_more_selected_photos" + style="@style/PermissionGrantButtonAllowMorePhotosMaterial3" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_selected_photos_button" + android:text="@string/grant_dialog_button_allow_selected_photos" + style="@style/PermissionGrantButtonAllowSelectedPhotosMaterial3" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton android:id="@+id/permission_deny_button" android:text="@string/grant_dialog_button_deny" style="@style/PermissionGrantButtonDenyMaterial3" /> <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_dont_allow_more_selected_photos_button" + android:text="@string/grant_dialog_button_dont_allow_more_selected_photos" + style="@style/PermissionGrantButtonDontAllowMorePhotosMaterial3" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton android:id="@+id/permission_deny_and_dont_ask_again_button" android:text="@string/grant_dialog_button_deny" style="@style/PermissionGrantButtonDenyMaterial3" /> diff --git a/PermissionController/res/raw-night/coarse_loc_off.json b/PermissionController/res/raw-night/coarse_loc_off.json new file mode 100644 index 000000000..2779a0c64 --- /dev/null +++ b/PermissionController/res/raw-night/coarse_loc_off.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Approximate_OFF_DT","ddd":0,"assets":[{"id":"comp_0","nm":"Approximate_OFF_DT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[62,62,100]},{"t":60,"s":[90,90,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Highway_Signs","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,67.277],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.48,-0.218],[-0.001,3.653],[5.479,-0.218],[5.999,-3.653],[-6,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.583,71.205],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,65.956],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,70.159],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.218],[0.001,3.653],[5.48,-0.218],[5.999,-3.653],[-5.999,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,74.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,68.837],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,222.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.217],[-0.001,3.653],[5.48,-0.217],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,226.283],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.038],[0,0],[0.292,-1.238],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.238]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,221.035],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.177]],"o":[[-0.042,0.177],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.276],[-6,0.275],[6,0.275],[5.894,-0.276]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,186.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.192],[0,0],[-1.626,3.1],[0.158,1.028],[0,0]],"o":[[1.626,3.1],[0,0],[0.626,-1.192],[0,0],[-0.158,1.028]],"v":[[-5.479,-0.218],[-0.001,3.652],[5.48,-0.218],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.01,190.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.036],[0,0],[1.358,0.038],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.036],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,185.218],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".yellow200","cl":"yellow200","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10.273,-62.792],[10.273,-19.932],[5.308,43.982],[3.529,62.792]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.691,67.791],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0]],"v":[[-69.845,50.02],[-43.615,20.333],[-43.615,-21.96],[-34.261,-28.874],[-24.095,-28.874],[-16.368,-34.974],[-1.728,-31.72],[1.931,-37.821],[7.218,-34.567],[23.079,-42.293],[55.331,-42.293],[69.845,-50.02]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[373.948,242.314],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-113.972,-119.335],[-113.972,-89.146],[-110.288,-76.336],[-118.014,-57.426],[-110.288,-39.939],[-93.615,-15.54],[-81.414,-1.306],[-84.668,17.808],[-75.721,29.193],[-63.521,36.921],[-49.288,39.36],[-41.968,48.714],[-28.954,48.714],[-17.568,53.594],[-2.115,58.067],[5.205,65.794],[21.066,68.233],[51.972,97.107],[62.545,99.954],[75.559,99.954],[118.014,119.335]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.421,175.3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-214.415,-10.167],[-193.269,2.034],[-180.255,2.034],[-172.122,4.88],[-161.548,-0.406],[-149.349,0.001],[-132.675,-7.32],[-126.575,-5.693],[-113.969,-10.167],[-96.075,-8.133],[-88.755,-2.643],[-80.215,-2.643],[-43.208,-2.643],[-22.062,-6.1],[-13.319,0.407],[-0.61,2.034],[17.384,-2.643],[35.278,-0.406],[50.731,2.034],[53.985,6.913],[73.912,4.88],[86.111,10.167],[114.578,10.167],[150.365,10.167],[159.311,4.88],[214.415,2.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[223.889,101.607],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.296,-34.933],[-152.296,-17.527],[-140.503,-1.668],[-129.523,3.62],[-129.523,10.94],[-120.577,15.413],[-117.729,22.327],[-88.449,34.933],[-76.251,30.867],[-68.117,23.953],[-38.43,27.207],[-25.417,28.833],[-7.93,24.767],[20.943,25.986],[31.924,29.24],[38.837,25.58],[51.443,25.58],[60.391,21.513],[86.01,18.667],[97.804,26.8],[152.297,26.393]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.103,105.308],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[21.757,-76.408],[21.757,-63.89],[13.214,-63.89],[-14.031,-38.677],[-19.724,-23.223],[-21.757,54.857],[-18.098,63.803],[-21.757,76.409]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,177.698],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-67.1,-42.533],[-37.413,-29.478],[-34.567,-20.938],[-28.873,-24.191],[-17.08,-23.378],[-5.693,-7.518],[12.2,-3.046],[18.3,5.495],[24.807,9.562],[38.227,11.594],[52.867,19.729],[62.525,21.355],[67.1,29.896],[67.1,42.533]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[160.753,59.919],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-139.893,-158.396],[-135.827,-143.349],[-95.566,-100.243],[-75.233,-93.736],[-51.646,-67.303],[-48.8,-54.29],[-35.38,-42.496],[-32.94,-22.57],[-24.807,-18.91],[-10.574,-14.844],[8.947,16.47],[58.56,53.477],[82.147,84.79],[102.074,96.99],[125.66,133.997],[139.893,142.536],[139.893,158.396]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.307,169.317],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey300","cl":"grey300","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[31.717,-1.007],[-31.717,1.007]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[287.883,119.11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-35.365,-14.212],[-27.159,-14.715],[-22.239,-18.741],[-8.414,-18.741],[6.556,-19.629],[15.02,-18.202],[28.069,-14.212],[28.069,-5.406],[28.069,1.64],[28.069,10.949],[29.704,14.22],[35.239,16.731],[35.365,19.629]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[291.531,123.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.561,-23.371],[-37.444,-22.113],[-31.154,-25.384],[-26.499,-26.767],[-21.215,-28.151],[-18.196,-31.05],[-12.41,-31.528],[-8.761,-31.05],[-7.628,-28.906],[9.733,-22.113],[23.067,-5.129],[29.106,-0.14],[37.283,12.608],[40.931,21.004],[41.561,31.528]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[348.112,171.29],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[4.64,-38.155],[4.64,-8.29],[7.771,-2.101],[6.974,5.21],[2.462,11.432],[-0.966,22.989],[-7.771,38.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[251.527,139.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-19.621,11.458],[-23.788,-11.458],[23.788,-11.458]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[259.299,233.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-36.573,-3.452],[3.542,-3.452],[8.492,-1.858],[25.051,-3.452],[31.026,-2.655],[35.293,0.133],[36.573,2.352],[34.013,2.352],[32.887,3.451]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[297.973,269.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-25.691,-18.223],[-10.328,-18.734],[-5.207,-17.88],[1.281,-18.307],[18.009,-19.077],[25.691,-12.504],[22.789,-10.797],[14.168,0.47],[12.376,5.846],[8.45,13.615],[4.011,19.077]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[307.575,287.115],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.742,46.448],[51.669,43.468],[50.742,37.157],[55.148,32.302],[72.276,31.185],[72.276,5.64],[70.623,-1.526],[70.623,-40.523],[69.383,-42.865],[62.906,-43.554],[56.843,-42.865],[54.224,-44.381],[49.264,-44.381],[45.13,-46.448],[-72.276,-46.448]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[226.967,241.72],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-119.509,-44.611],[-97.824,-40.96],[-89.824,-34.688],[-72.741,-36.266],[-66.754,-33.154],[40.537,-34.971],[59.633,-38.882],[68.047,-38.882],[73.387,-37.306],[74.626,-33.154],[77.919,-31.804],[77.919,-26.232],[80.508,-24.928],[89.57,-21.215],[97.823,-16.038],[105.684,-14.257],[108.827,-8.431],[113.844,5.663],[119.508,8.884],[117.654,19.078],[110.284,25.092],[107.048,37.527],[104.562,44.612]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[278.637,267.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-51.352,2.509],[13.052,3.155],[35.386,-3.155],[51.352,-3.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.911,213.904],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[275.481,202.819],[275.481,222.082]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 39","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.805,-24.131],[-0.446,-14.804],[1.18,18.209],[-2.805,24.131]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[269.292,219.403],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-16.325,-7.523],[-4.713,-0.839],[5.806,-0.839],[16.325,7.523]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[261.745,182.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-52.188,-19.67],[-41.346,-24.51],[-9.628,-12.712],[5.098,-9.312],[20.956,-3.564],[26.135,-4.62],[34.719,-0.671],[41.671,4.326],[52.189,7.729],[46.039,15.452],[46.202,24.509]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[213.259,163.422],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[73.548,-6.19],[75.996,7.565],[69.525,24.299],[59.976,26.175],[53.504,28.441],[47.03,33.188],[47.03,39.488],[34.894,32.698],[26.767,21.986],[17.423,17.347],[-1.49,12.605],[-28.757,3.71],[-37.055,-16.252],[-66.354,-16.252],[-66.354,-18.782],[-69.801,-23.38],[-68.077,-26.092],[-70.654,-30.31],[-74.309,-30.31],[-75.996,-39.488]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[230.554,138.453],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-68.814,-67.689],[-33.186,-67.127],[-13.566,-64.287],[9.77,-52.647],[17.081,-46.04],[58.129,-44.971],[60.347,-29.873],[60.347,-12.806],[61.785,6.876],[59.395,9.068],[60.347,54.334],[65.44,54.334],[68.814,58.411],[63.612,63.472],[65.581,64.877],[63.612,67.689]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.756,235.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-6.636,-10.36],[-2.642,-10.95],[4.106,-13.762],[4.478,-12.242],[5.933,-6.307],[6.636,13.761]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[170.836,181.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[150.903,178.874],[176.77,178.874]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 46","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-46.11,-24.495],[-24.601,-24.495],[-18.978,-22.285],[10.012,-24.495],[26.288,-21.262],[39.924,-19.433],[46.11,-14.795],[41.028,24.495]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[194.482,197.586],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[163.403,74.086],[163.403,98.964]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 48","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.298,-13.017],[-2.298,-7.952],[0.038,-1.57],[0.038,13.017]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.776,74.953],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-25.094,-4.83],[-14.691,-5.955],[-13.004,-4.127],[-6.116,-5.041],[25.094,-5.041],[25.094,5.955]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[113.72,93.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-21.799,-26.171],[-21.799,13.635],[-20.774,20.008],[16.176,26.171],[21.799,24.503]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[143.443,79.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 51","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[88.626,74.857],[151.806,74.857]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 52","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[93.653,52.401],[93.653,94.366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 53","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.723,-35.415],[-1.723,-14.469],[0.62,-12.953],[1.723,35.415]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[162.477,159.858],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-11.575,3.259],[-0.551,-3.259],[4.821,-3.259],[7.166,-1.191],[9.784,-1.191],[11.575,-3.259]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[155.932,166.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-4.489,-23.839],[-6.05,-13.917],[-7.841,-12.401],[-7.841,1.329],[-11.562,10.887],[-11.562,23.84],[11.562,23.84]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.855000035903,0.862999949736,0.877999997606,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[179.069,138.912],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey700","cl":"grey700","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-84.172,-14.163],[-87.624,-10.786],[-87.624,-7.25],[-91.245,-3.685],[-94.866,-5.154],[-98.486,-11.792],[-101.705,-10.384],[-105.729,-11.792],[-110.154,-14.163],[-116.994,-11.792],[-121.218,-5.355],[-119.247,-2.337],[-117.598,-2.337],[-116.19,1.083],[-110.959,2.286],[-107.137,3.899],[-97.49,3.899],[-97.49,10.725],[-88.428,11.544],[-86.819,19.59],[-68.714,22.205],[-62.64,26.027],[-55.235,33.068],[-45.78,31.79],[-35.723,31.79],[-22.848,31.79],[-17.818,34.678],[-3.957,38.5],[5.315,37.695],[13.563,34.678],[23.622,33.873],[46.757,36.891],[58.826,36.891],[67.678,33.068],[75.14,30.654],[87.996,32.666],[90.209,31.79],[108.313,29.649],[117.768,25.022],[130.243,25.424],[144.523,25.022],[148.34,20.596],[149.955,15.969],[148.34,10.725],[127.193,-42.264],[-25.865,-48.73],[-69.318,-44.784],[-72.134,-37.519],[-82.394,-31.305]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.06,59.651],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 57","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-53.521,-13.307],[-41.048,-20.863],[-32.801,-18.738],[-26.967,-20.863],[-15.902,-15.576],[-11.075,-15.576],[-11.075,-13.307],[4.818,-13.307],[6.83,-15.576],[17.29,-15.576],[20.308,-23.708],[24.532,-22.041],[35.194,-20.863],[45.454,-19.945],[47.264,-15.576],[52.695,-6.064],[46.459,23.708],[42.881,20.101],[34.646,17.883],[-4.626,17.883],[-13.177,20.101],[-23.946,20.101],[-30.387,15.26],[-38.835,16.264],[-49.296,17.271],[-56.009,14.456]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.974,127.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 61","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.851,0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[2.851,-0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-209.239,-23.701],[-197.204,-12.299],[-193.403,-15.149],[-193.403,-18.95],[-181.051,-21.801],[-177.251,-23.701],[-166.482,-23.701],[-157.931,-25.918],[-154.447,-25.918],[-135.761,-25.918],[-131.327,-25.918],[-124.36,-25.918],[-118.658,-25.918],[-110.424,-23.701],[-104.406,-19.108],[-98.389,-12.299],[-88.254,0.49],[-84.77,0],[-84.77,-6.282],[-81.92,-1.531],[-83.351,0],[-84.736,0.49],[-86.024,1.81],[-86.644,2.239],[-81.92,12.262],[-74.427,29.3],[-73.52,32.545],[-69.607,41.517],[-69.607,44.142],[-68.7,45.383],[-66.791,56.121],[-66.791,60.226],[-68.748,61.371],[-68.748,62.564],[-70.322,63.995],[-70.37,65.523],[-72.327,67.146],[-73.615,67.146],[-74.14,68.195],[-75.477,68.195],[-75.477,69.389],[-76.908,70.439],[-77.481,71.059],[-77.481,72.014],[-78.293,72.872],[-76.813,73.445],[-76.67,74.877],[-75.572,75.402],[-75.668,77.788],[-74.904,77.788],[-74.904,81.367],[-74.093,81.367],[-73.854,83.611],[-72.947,84.231],[-72.947,85.52],[-71.754,86.331],[-69.655,86.331],[-64.643,86.331],[-64.023,85.472],[-61.302,85.902],[-61.064,86.331],[-57.485,86.665],[-56.005,88.287],[-56.005,89.815],[-54.287,90.912],[-53.428,90.244],[-52.998,90.912],[-51.614,91.199],[-51.614,92.249],[-49.133,92.249],[-48.751,93.299],[-46.651,93.299],[-45.935,94.253],[-45.076,94.253],[-42.833,96.688],[-40.256,96.688],[-40.256,97.498],[-38.108,97.498],[-37.678,98.501],[-35.817,98.501],[-34.815,99.169],[-34.004,99.169],[-33.622,98.453],[-31.14,98.453],[-31.427,97.642],[-31.904,96.782],[-33.431,96.735],[-33.622,91.58],[-33.097,91.58],[-32.763,92.583],[-31.713,92.44],[-31.14,92.965],[-29.279,92.869],[-29.279,91.676],[-30.138,91.342],[-30.043,89.385],[-30.902,88.669],[-30.949,87.237],[-31.904,86.808],[-30.997,86.092],[-30.854,84.66],[-30.138,84.374],[-30.091,80.556],[-31.045,79.984],[-30.759,76.691],[-29.47,78.313],[-29.279,79.268],[-27.37,80.222],[-26.559,79.888],[-25.89,78.934],[-25.604,77.884],[-24.459,76.357],[-21.882,75.402],[-17.586,74.973],[-14.771,74.734],[-14.771,75.831],[-22.072,77.74],[-23.361,78.122],[-24.268,78.6],[-25.127,79.124],[-25.509,80.174],[-27.036,81.272],[-28.181,81.749],[-29.088,83.229],[-29.088,85.997],[-28.849,87.953],[-27.752,88.287],[-26.177,89.385],[-24.173,88.097],[-23.648,88.097],[-22.502,87.143],[-22.74,86.331],[-23.027,83.372],[-22.12,83.133],[-21.548,83.897],[-21.5,84.518],[-20.88,84.66],[-20.88,85.902],[-20.02,86.188],[-19.734,85.328],[-16.918,85.568],[-16.966,84.708],[-19.114,82.895],[-19.591,82.035],[-19.495,81.272],[-16.441,81.415],[-16.107,80.509],[-13.72,80.509],[-12.862,79.076],[-11.239,79.984],[-10.809,83.849],[-9.473,83.897],[-8.996,84.566],[-6.848,84.852],[-6.085,85.949],[-3.364,86.236],[-3.078,84.756],[-3.698,81.701],[-6.037,79.554],[-6.323,78.504],[-3.603,78.456],[-2.219,78.027],[-0.406,77.359],[0.549,77.74],[2.84,77.645],[4.94,77.645],[6.133,78.027],[8.567,78.6],[9.14,79.458],[11.096,79.458],[11.43,80.413],[13.149,80.413],[13.435,80.986],[14.628,81.033],[14.628,80.413],[15.868,79.936],[15.868,78.934],[16.346,81.272],[18.064,81.32],[18.064,80.222],[17.539,80.174],[17.539,78.074],[18.446,78.552],[19.973,80.031],[21.452,80.079],[21.452,82.704],[20.976,83.42],[20.976,84.518],[19.782,84.804],[19.83,85.71],[22.026,86.188],[22.884,86.999],[24.698,87.047],[25.414,87.953],[27.275,87.953],[30.855,87.953],[31.523,86.999],[32.764,87.047],[32.286,88.335],[31.761,88.956],[32.048,90.436],[36.915,90.818],[37.583,98.787],[39.97,102.366],[42.738,103.368],[44.552,106.185],[54.431,117.018],[58.582,120.216],[67.889,127.518],[73.711,130.429],[85.165,134.294],[88.458,136.013],[91.179,136.013],[97.717,142.026],[98.338,144.89],[104.494,147.849],[107.262,150],[211.975,150],[211.975,-151.69],[-210.962,-153.764]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,171.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 62","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,148.684],[206,148.684],[206,-148.684],[-206,-148.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,172.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 63","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.66,0.66],"y":[0,0]},"t":0,"s":[294,294]},{"t":10,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.66],"y":[0]},"t":0,"s":[6]},{"t":10,"s":[0]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":20,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[64.361,64.36,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[150,150,100]},{"t":45,"s":[48.034,48.034,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.829000016755,0.885999971278,0.955999995213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[100]},{"t":32,"s":[70]}],"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.552941203117,0.670588254929,0.921568632126,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[50]},{"t":32,"s":[20]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"CircleMatte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Approximate_OFF_DT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.16,307.16,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[{"tm":91,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/PermissionController/res/raw-night/coarse_loc_on.json b/PermissionController/res/raw-night/coarse_loc_on.json new file mode 100644 index 000000000..b6f098d30 --- /dev/null +++ b/PermissionController/res/raw-night/coarse_loc_on.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Approximate_ON_DT","ddd":0,"assets":[{"id":"comp_0","nm":"Approximate_ON_DT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[90,90,100]},{"t":60,"s":[62,62,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Highway_Signs","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,67.277],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.48,-0.218],[-0.001,3.653],[5.479,-0.218],[5.999,-3.653],[-6,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.583,71.205],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,65.956],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,70.159],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.218],[0.001,3.653],[5.48,-0.218],[5.999,-3.653],[-5.999,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,74.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,68.837],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,222.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.217],[-0.001,3.653],[5.48,-0.217],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,226.283],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.038],[0,0],[0.292,-1.238],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.238]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,221.035],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.177]],"o":[[-0.042,0.177],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.276],[-6,0.275],[6,0.275],[5.894,-0.276]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,186.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.192],[0,0],[-1.626,3.1],[0.158,1.028],[0,0]],"o":[[1.626,3.1],[0,0],[0.626,-1.192],[0,0],[-0.158,1.028]],"v":[[-5.479,-0.218],[-0.001,3.652],[5.48,-0.218],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.01,190.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.036],[0,0],[1.358,0.038],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.036],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,185.218],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".yellow200","cl":"yellow200","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10.273,-62.792],[10.273,-19.932],[5.308,43.982],[3.529,62.792]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.691,67.791],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0]],"v":[[-69.845,50.02],[-43.615,20.333],[-43.615,-21.96],[-34.261,-28.874],[-24.095,-28.874],[-16.368,-34.974],[-1.728,-31.72],[1.931,-37.821],[7.218,-34.567],[23.079,-42.293],[55.331,-42.293],[69.845,-50.02]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[373.948,242.314],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-113.972,-119.335],[-113.972,-89.146],[-110.288,-76.336],[-118.014,-57.426],[-110.288,-39.939],[-93.615,-15.54],[-81.414,-1.306],[-84.668,17.808],[-75.721,29.193],[-63.521,36.921],[-49.288,39.36],[-41.968,48.714],[-28.954,48.714],[-17.568,53.594],[-2.115,58.067],[5.205,65.794],[21.066,68.233],[51.972,97.107],[62.545,99.954],[75.559,99.954],[118.014,119.335]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.421,175.3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-214.415,-10.167],[-193.269,2.034],[-180.255,2.034],[-172.122,4.88],[-161.548,-0.406],[-149.349,0.001],[-132.675,-7.32],[-126.575,-5.693],[-113.969,-10.167],[-96.075,-8.133],[-88.755,-2.643],[-80.215,-2.643],[-43.208,-2.643],[-22.062,-6.1],[-13.319,0.407],[-0.61,2.034],[17.384,-2.643],[35.278,-0.406],[50.731,2.034],[53.985,6.913],[73.912,4.88],[86.111,10.167],[114.578,10.167],[150.365,10.167],[159.311,4.88],[214.415,2.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[223.889,101.607],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.296,-34.933],[-152.296,-17.527],[-140.503,-1.668],[-129.523,3.62],[-129.523,10.94],[-120.577,15.413],[-117.729,22.327],[-88.449,34.933],[-76.251,30.867],[-68.117,23.953],[-38.43,27.207],[-25.417,28.833],[-7.93,24.767],[20.943,25.986],[31.924,29.24],[38.837,25.58],[51.443,25.58],[60.391,21.513],[86.01,18.667],[97.804,26.8],[152.297,26.393]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.103,105.308],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[21.757,-76.408],[21.757,-63.89],[13.214,-63.89],[-14.031,-38.677],[-19.724,-23.223],[-21.757,54.857],[-18.098,63.803],[-21.757,76.409]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,177.698],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-67.1,-42.533],[-37.413,-29.478],[-34.567,-20.938],[-28.873,-24.191],[-17.08,-23.378],[-5.693,-7.518],[12.2,-3.046],[18.3,5.495],[24.807,9.562],[38.227,11.594],[52.867,19.729],[62.525,21.355],[67.1,29.896],[67.1,42.533]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[160.753,59.919],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-139.893,-158.396],[-135.827,-143.349],[-95.566,-100.243],[-75.233,-93.736],[-51.646,-67.303],[-48.8,-54.29],[-35.38,-42.496],[-32.94,-22.57],[-24.807,-18.91],[-10.574,-14.844],[8.947,16.47],[58.56,53.477],[82.147,84.79],[102.074,96.99],[125.66,133.997],[139.893,142.536],[139.893,158.396]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156862745,0.886274509804,0.576470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.307,169.317],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey300","cl":"grey300","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[31.717,-1.007],[-31.717,1.007]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[287.883,119.11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-35.365,-14.212],[-27.159,-14.715],[-22.239,-18.741],[-8.414,-18.741],[6.556,-19.629],[15.02,-18.202],[28.069,-14.212],[28.069,-5.406],[28.069,1.64],[28.069,10.949],[29.704,14.22],[35.239,16.731],[35.365,19.629]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[291.531,123.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.561,-23.371],[-37.444,-22.113],[-31.154,-25.384],[-26.499,-26.767],[-21.215,-28.151],[-18.196,-31.05],[-12.41,-31.528],[-8.761,-31.05],[-7.628,-28.906],[9.733,-22.113],[23.067,-5.129],[29.106,-0.14],[37.283,12.608],[40.931,21.004],[41.561,31.528]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[348.112,171.29],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[4.64,-38.155],[4.64,-8.29],[7.771,-2.101],[6.974,5.21],[2.462,11.432],[-0.966,22.989],[-7.771,38.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[251.527,139.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-19.621,11.458],[-23.788,-11.458],[23.788,-11.458]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[259.299,233.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-36.573,-3.452],[3.542,-3.452],[8.492,-1.858],[25.051,-3.452],[31.026,-2.655],[35.293,0.133],[36.573,2.352],[34.013,2.352],[32.887,3.451]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[297.973,269.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-25.691,-18.223],[-10.328,-18.734],[-5.207,-17.88],[1.281,-18.307],[18.009,-19.077],[25.691,-12.504],[22.789,-10.797],[14.168,0.47],[12.376,5.846],[8.45,13.615],[4.011,19.077]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[307.575,287.115],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.742,46.448],[51.669,43.468],[50.742,37.157],[55.148,32.302],[72.276,31.185],[72.276,5.64],[70.623,-1.526],[70.623,-40.523],[69.383,-42.865],[62.906,-43.554],[56.843,-42.865],[54.224,-44.381],[49.264,-44.381],[45.13,-46.448],[-72.276,-46.448]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[226.967,241.72],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-119.509,-44.611],[-97.824,-40.96],[-89.824,-34.688],[-72.741,-36.266],[-66.754,-33.154],[40.537,-34.971],[59.633,-38.882],[68.047,-38.882],[73.387,-37.306],[74.626,-33.154],[77.919,-31.804],[77.919,-26.232],[80.508,-24.928],[89.57,-21.215],[97.823,-16.038],[105.684,-14.257],[108.827,-8.431],[113.844,5.663],[119.508,8.884],[117.654,19.078],[110.284,25.092],[107.048,37.527],[104.562,44.612]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[278.637,267.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-51.352,2.509],[13.052,3.155],[35.386,-3.155],[51.352,-3.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.911,213.904],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[275.481,202.819],[275.481,222.082]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 39","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.805,-24.131],[-0.446,-14.804],[1.18,18.209],[-2.805,24.131]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[269.292,219.403],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-16.325,-7.523],[-4.713,-0.839],[5.806,-0.839],[16.325,7.523]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[261.745,182.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-52.188,-19.67],[-41.346,-24.51],[-9.628,-12.712],[5.098,-9.312],[20.956,-3.564],[26.135,-4.62],[34.719,-0.671],[41.671,4.326],[52.189,7.729],[46.039,15.452],[46.202,24.509]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[213.259,163.422],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[73.548,-6.19],[75.996,7.565],[69.525,24.299],[59.976,26.175],[53.504,28.441],[47.03,33.188],[47.03,39.488],[34.894,32.698],[26.767,21.986],[17.423,17.347],[-1.49,12.605],[-28.757,3.71],[-37.055,-16.252],[-66.354,-16.252],[-66.354,-18.782],[-69.801,-23.38],[-68.077,-26.092],[-70.654,-30.31],[-74.309,-30.31],[-75.996,-39.488]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[230.554,138.453],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-68.814,-67.689],[-33.186,-67.127],[-13.566,-64.287],[9.77,-52.647],[17.081,-46.04],[58.129,-44.971],[60.347,-29.873],[60.347,-12.806],[61.785,6.876],[59.395,9.068],[60.347,54.334],[65.44,54.334],[68.814,58.411],[63.612,63.472],[65.581,64.877],[63.612,67.689]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.756,235.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-6.636,-10.36],[-2.642,-10.95],[4.106,-13.762],[4.478,-12.242],[5.933,-6.307],[6.636,13.761]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[170.836,181.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[150.903,178.874],[176.77,178.874]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 46","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-46.11,-24.495],[-24.601,-24.495],[-18.978,-22.285],[10.012,-24.495],[26.288,-21.262],[39.924,-19.433],[46.11,-14.795],[41.028,24.495]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[194.482,197.586],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[163.403,74.086],[163.403,98.964]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 48","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.298,-13.017],[-2.298,-7.952],[0.038,-1.57],[0.038,13.017]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.776,74.953],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-25.094,-4.83],[-14.691,-5.955],[-13.004,-4.127],[-6.116,-5.041],[25.094,-5.041],[25.094,5.955]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[113.72,93.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-21.799,-26.171],[-21.799,13.635],[-20.774,20.008],[16.176,26.171],[21.799,24.503]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[143.443,79.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 51","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[88.626,74.857],[151.806,74.857]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 52","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[93.653,52.401],[93.653,94.366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 53","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.723,-35.415],[-1.723,-14.469],[0.62,-12.953],[1.723,35.415]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[162.477,159.858],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-11.575,3.259],[-0.551,-3.259],[4.821,-3.259],[7.166,-1.191],[9.784,-1.191],[11.575,-3.259]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[155.932,166.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-4.489,-23.839],[-6.05,-13.917],[-7.841,-12.401],[-7.841,1.329],[-11.562,10.887],[-11.562,23.84],[11.562,23.84]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[179.069,138.912],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey700","cl":"grey700","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-84.172,-14.163],[-87.624,-10.786],[-87.624,-7.25],[-91.245,-3.685],[-94.866,-5.154],[-98.486,-11.792],[-101.705,-10.384],[-105.729,-11.792],[-110.154,-14.163],[-116.994,-11.792],[-121.218,-5.355],[-119.247,-2.337],[-117.598,-2.337],[-116.19,1.083],[-110.959,2.286],[-107.137,3.899],[-97.49,3.899],[-97.49,10.725],[-88.428,11.544],[-86.819,19.59],[-68.714,22.205],[-62.64,26.027],[-55.235,33.068],[-45.78,31.79],[-35.723,31.79],[-22.848,31.79],[-17.818,34.678],[-3.957,38.5],[5.315,37.695],[13.563,34.678],[23.622,33.873],[46.757,36.891],[58.826,36.891],[67.678,33.068],[75.14,30.654],[87.996,32.666],[90.209,31.79],[108.313,29.649],[117.768,25.022],[130.243,25.424],[144.523,25.022],[148.34,20.596],[149.955,15.969],[148.34,10.725],[127.193,-42.264],[-25.865,-48.73],[-69.318,-44.784],[-72.134,-37.519],[-82.394,-31.305]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.06,59.651],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 57","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-53.521,-13.307],[-41.048,-20.863],[-32.801,-18.738],[-26.967,-20.863],[-15.902,-15.576],[-11.075,-15.576],[-11.075,-13.307],[4.818,-13.307],[6.83,-15.576],[17.29,-15.576],[20.308,-23.708],[24.532,-22.041],[35.194,-20.863],[45.454,-19.945],[47.264,-15.576],[52.695,-6.064],[46.459,23.708],[42.881,20.101],[34.646,17.883],[-4.626,17.883],[-13.177,20.101],[-23.946,20.101],[-30.387,15.26],[-38.835,16.264],[-49.296,17.271],[-56.009,14.456]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.974,127.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 61","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.851,0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[2.851,-0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-209.239,-23.701],[-197.204,-12.299],[-193.403,-15.149],[-193.403,-18.95],[-181.051,-21.801],[-177.251,-23.701],[-166.482,-23.701],[-157.931,-25.918],[-154.447,-25.918],[-135.761,-25.918],[-131.327,-25.918],[-124.36,-25.918],[-118.658,-25.918],[-110.424,-23.701],[-104.406,-19.108],[-98.389,-12.299],[-88.254,0.49],[-84.77,0],[-84.77,-6.282],[-81.92,-1.531],[-83.351,0],[-84.736,0.49],[-86.024,1.81],[-86.644,2.239],[-81.92,12.262],[-74.427,29.3],[-73.52,32.545],[-69.607,41.517],[-69.607,44.142],[-68.7,45.383],[-66.791,56.121],[-66.791,60.226],[-68.748,61.371],[-68.748,62.564],[-70.322,63.995],[-70.37,65.523],[-72.327,67.146],[-73.615,67.146],[-74.14,68.195],[-75.477,68.195],[-75.477,69.389],[-76.908,70.439],[-77.481,71.059],[-77.481,72.014],[-78.293,72.872],[-76.813,73.445],[-76.67,74.877],[-75.572,75.402],[-75.668,77.788],[-74.904,77.788],[-74.904,81.367],[-74.093,81.367],[-73.854,83.611],[-72.947,84.231],[-72.947,85.52],[-71.754,86.331],[-69.655,86.331],[-64.643,86.331],[-64.023,85.472],[-61.302,85.902],[-61.064,86.331],[-57.485,86.665],[-56.005,88.287],[-56.005,89.815],[-54.287,90.912],[-53.428,90.244],[-52.998,90.912],[-51.614,91.199],[-51.614,92.249],[-49.133,92.249],[-48.751,93.299],[-46.651,93.299],[-45.935,94.253],[-45.076,94.253],[-42.833,96.688],[-40.256,96.688],[-40.256,97.498],[-38.108,97.498],[-37.678,98.501],[-35.817,98.501],[-34.815,99.169],[-34.004,99.169],[-33.622,98.453],[-31.14,98.453],[-31.427,97.642],[-31.904,96.782],[-33.431,96.735],[-33.622,91.58],[-33.097,91.58],[-32.763,92.583],[-31.713,92.44],[-31.14,92.965],[-29.279,92.869],[-29.279,91.676],[-30.138,91.342],[-30.043,89.385],[-30.902,88.669],[-30.949,87.237],[-31.904,86.808],[-30.997,86.092],[-30.854,84.66],[-30.138,84.374],[-30.091,80.556],[-31.045,79.984],[-30.759,76.691],[-29.47,78.313],[-29.279,79.268],[-27.37,80.222],[-26.559,79.888],[-25.89,78.934],[-25.604,77.884],[-24.459,76.357],[-21.882,75.402],[-17.586,74.973],[-14.771,74.734],[-14.771,75.831],[-22.072,77.74],[-23.361,78.122],[-24.268,78.6],[-25.127,79.124],[-25.509,80.174],[-27.036,81.272],[-28.181,81.749],[-29.088,83.229],[-29.088,85.997],[-28.849,87.953],[-27.752,88.287],[-26.177,89.385],[-24.173,88.097],[-23.648,88.097],[-22.502,87.143],[-22.74,86.331],[-23.027,83.372],[-22.12,83.133],[-21.548,83.897],[-21.5,84.518],[-20.88,84.66],[-20.88,85.902],[-20.02,86.188],[-19.734,85.328],[-16.918,85.568],[-16.966,84.708],[-19.114,82.895],[-19.591,82.035],[-19.495,81.272],[-16.441,81.415],[-16.107,80.509],[-13.72,80.509],[-12.862,79.076],[-11.239,79.984],[-10.809,83.849],[-9.473,83.897],[-8.996,84.566],[-6.848,84.852],[-6.085,85.949],[-3.364,86.236],[-3.078,84.756],[-3.698,81.701],[-6.037,79.554],[-6.323,78.504],[-3.603,78.456],[-2.219,78.027],[-0.406,77.359],[0.549,77.74],[2.84,77.645],[4.94,77.645],[6.133,78.027],[8.567,78.6],[9.14,79.458],[11.096,79.458],[11.43,80.413],[13.149,80.413],[13.435,80.986],[14.628,81.033],[14.628,80.413],[15.868,79.936],[15.868,78.934],[16.346,81.272],[18.064,81.32],[18.064,80.222],[17.539,80.174],[17.539,78.074],[18.446,78.552],[19.973,80.031],[21.452,80.079],[21.452,82.704],[20.976,83.42],[20.976,84.518],[19.782,84.804],[19.83,85.71],[22.026,86.188],[22.884,86.999],[24.698,87.047],[25.414,87.953],[27.275,87.953],[30.855,87.953],[31.523,86.999],[32.764,87.047],[32.286,88.335],[31.761,88.956],[32.048,90.436],[36.915,90.818],[37.583,98.787],[39.97,102.366],[42.738,103.368],[44.552,106.185],[54.431,117.018],[58.582,120.216],[67.889,127.518],[73.711,130.429],[85.165,134.294],[88.458,136.013],[91.179,136.013],[97.717,142.026],[98.338,144.89],[104.494,147.849],[107.262,150],[211.975,150],[211.975,-151.69],[-210.962,-153.764]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,171.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 62","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,148.684],[206,148.684],[206,-148.684],[-206,-148.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,172.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 63","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.34,0.34],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[300,300]},{"t":25,"s":[294,294]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.552941203117,0.670588254929,0.921568632126,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":25,"s":[6]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Pale Orange Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Blue Circle Outlines","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[64.361,64.36,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[66.714,66.714,100]},{"t":45,"s":[208.333,208.333,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.829000016755,0.885999971278,0.955999995213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[70]},{"t":21,"s":[100]}],"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0,0,0,1]},{"t":21,"s":[0.552941203117,0.670588254929,0.921568632126,1]}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[20]},{"t":21,"s":[50]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"CircleMatte 4","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Approximate_ON_DT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/PermissionController/res/raw-night/fine_loc_off.json b/PermissionController/res/raw-night/fine_loc_off.json new file mode 100644 index 000000000..559cafa58 --- /dev/null +++ b/PermissionController/res/raw-night/fine_loc_off.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Exact_OFF_DT","ddd":0,"assets":[{"id":"comp_0","nm":"Exact_OFF_DT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[102,102,100]},{"t":60,"s":[80,80,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Location Pointer Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[18.25,51.679,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.967,0.967,0.2],"y":[1,1,1]},"o":{"x":[0.499,0.499,0.4],"y":[0,0,0]},"t":24,"s":[54,54,100]},{"t":42,"s":[10,10,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.551,0],[0,-3.55],[3.55,0],[0,3.551]],"o":[[3.55,0],[0,3.551],[-3.551,0],[0,-3.55]],"v":[[0.001,-6.429],[6.428,-0.001],[0.001,6.429],[-6.428,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","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},{"ty":"tr","p":{"a":0,"k":[18.249,18.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.952,0],[0,-9.951],[0,0],[0,13.5]],"o":[[-9.952,0],[0,13.5],[0,0],[0,-9.951]],"v":[[0,-25.715],[-18,-7.715],[0,25.715],[18,-7.715]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.25,25.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":43,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Location bottom Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[13.432,6.364,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.967,0.967,0.2],"y":[1,1,1]},"o":{"x":[0.499,0.499,0.4],"y":[0,0,0]},"t":24,"s":[54,54,100]},{"t":42,"s":[10,10,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":43,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Pins_Blue","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.201,-1.878],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.4,206.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.555,165.221],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[49.115,205.645],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[105.514,213.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.878],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.878],[0,-2.055]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[171.789,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[368.126,111.436],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[281.531,116.994],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[312.438,125.251],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 23","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[329.856,217.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Pins_Orange","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[27.703,137.9],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.744],[0,4.128],[3.303,-0.744]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[381.288,270.794],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[368.126,210.482],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.057],[-2.201,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.2,-1.878],[0,-2.057]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[350.754,184.962],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 27","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[183.575,115.565],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey300","cl":"grey300","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[65.394,-87.762],[21.956,-25.29],[-65.394,87.762]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[77.34,207.456],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-29.617,35.822],[12.473,-5.331],[29.618,-35.821]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[199.566,278.889],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.219,9.823],[8.218,-9.823]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[201.146,227.853],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-33.868,-25.038],[8.293,9.519],[33.867,25.038]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[209.364,226.554],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[19.167,15.54],[-19.167,-15.54]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[119.548,75.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-72.23,5.545],[32.282,-5.545],[55.773,-5.545],[72.23,5.545]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[387.97,276.339],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.098,-157.973],[9.854,-43.904],[-9.854,53.949],[-8.736,83.862],[-3.121,157.973]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[393.327,169.155],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-82.146,-18.015],[-17.286,18.015],[82.146,18.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[364.737,26.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[142.734,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[108.816,73.718],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[105.449,70.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-61.089,79.79],[-10.563,22.215],[30.571,-28.773],[46.604,-61.512],[61.089,-79.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[197.779,251.592],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-181.176,-130.71],[-103.059,-67.44],[-30.743,-4.641],[-18.758,22.842],[23.397,30.81],[65.134,54.857],[89.912,66.865],[181.176,130.71]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[252.512,144.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-197.801,-135.93],[-169.578,-81.272],[-25.091,29.988],[22.053,51.34],[69.798,92.618],[93.582,100.74],[139.02,95.547],[197.801,135.931]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.942,149.433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-43.312,-42.235],[22.43,13.494],[43.312,42.235]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[45.812,287.214],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[18.949,23.783],[-18.949,-23.783]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[109.621,305.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-161.26,59.553],[-73.09,134.19],[-18.949,92.406],[161.26,-134.191]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.76,147.693],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-170.929,-3.155],[-48.146,106.479],[7.347,155.978],[56.846,100.678],[93.971,24.495],[114.081,-34.03],[140.8,-83.029],[163.001,-98.674],[170.928,-155.978]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[176.522,171.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":"Group 35","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-128.003,-94.842],[128.003,94.842]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.569,230.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-143.858,-121.852],[-139.604,-101.518],[143.858,121.852]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.948,211.078],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-101.126,-61.295],[6.382,43.506],[25.329,50.08],[48.533,50.08],[101.126,61.295]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[339.523,72.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[75.549,33.199],[0.527,16.447],[-24.997,16.447],[-75.549,-33.199]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[358.139,131.247],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 39","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.267,11.975],[-22.236,-5.401],[-51.24,-5.401],[-56.267,-11.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[381.289,156.961],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-23.396,-110.585],[23.396,-69.624],[14.115,-1.562],[14.115,110.585]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.625,214.61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.301,-106.862],[-115.301,-91.911],[68.768,50.786],[98.546,63.161],[152.3,106.862]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.44,175.022],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[137.091,-155.846],[25.716,1.527],[6.767,17.403],[-27.65,61.101],[-76.956,108.281],[-137.091,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[205.333,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-56.46,-44.426],[56.46,44.426]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[221.768,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-157.626,-125.059],[157.625,125.059]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[276.914,138.562],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 46","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[128.583,-155.739],[-82.951,117.438],[-128.583,155.739]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[173.622,164.445],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[123.363,-155.846],[-123.363,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[149.452,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 48","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.688,-154.492],[-119.689,154.493]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.376,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.165,-121.947],[-94.165,121.948]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[102.853,137.12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey700","cl":"grey700","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.871,-11.834],[12.775,-0.847],[2.691,11.834],[-12.775,-0.336]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[260.038,79.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-35.787,-24.613],[-37.505,-22.753],[-34.63,-21.167],[-31.031,-23.281],[-9.88,-6.238],[-12.139,-3.2],[24.52,24.613],[37.505,7.844],[1.732,-21.035],[3.318,-23.281],[1.924,-24.613]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[231.647,43.549],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[37.533,-30.467],[28.951,-30.467],[20.804,-26.143],[14.324,-20.586],[4.618,-16.81],[1.118,-16.23],[-6.173,-7.064],[-37.533,17.139],[-22.607,30.467],[8.069,1.113],[26.699,-16.712]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[150.984,247.163],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey800","cl":"grey800","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,150],[206,150],[206,-150],[-206,-150]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.234999997008,0.250999989229,0.263000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.851,168.936],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 58","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Exact Light Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64.082,57.55,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,0],"ix":1,"l":2},"s":{"a":0,"k":[48.646,48.646,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.66,0.66],"y":[0,0]},"t":0,"s":[294,294]},{"t":10,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.552941203117,0.670588254929,0.921568632126,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.66],"y":[0]},"t":0,"s":[6]},{"t":10,"s":[0]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,149,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.6],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.8],"y":[0,0,0]},"t":0,"s":[306,306,100]},{"t":45,"s":[240,240,100]}],"ix":6,"l":2}},"ao":0,"sw":117,"sh":98,"sc":"#a77b57","ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"CircleMatte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Exact_OFF_DT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[{"tm":91,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/PermissionController/res/raw-night/fine_loc_on.json b/PermissionController/res/raw-night/fine_loc_on.json new file mode 100644 index 000000000..7731d7548 --- /dev/null +++ b/PermissionController/res/raw-night/fine_loc_on.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Exact_ON_DT","ddd":0,"assets":[{"id":"comp_0","nm":"Exact_ON_DT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[80,80,100]},{"t":60,"s":[102,102,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Location Pointer Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[18.25,51.679,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.552,0.552,0.667],"y":[1,1,1]},"o":{"x":[0.046,0.046,0.333],"y":[0,0,0]},"t":28,"s":[20,20,100]},{"i":{"x":[0.468,0.468,0.667],"y":[1,1,1]},"o":{"x":[0.441,0.441,0.333],"y":[0,0,0]},"t":42,"s":[58,58,100]},{"t":60,"s":[54,54,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.551,0],[0,-3.55],[3.55,0],[0,3.551]],"o":[[3.55,0],[0,3.551],[-3.551,0],[0,-3.55]],"v":[[0.001,-6.429],[6.428,-0.001],[0.001,6.429],[-6.428,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","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},{"ty":"tr","p":{"a":0,"k":[18.249,18.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.952,0],[0,-9.951],[0,0],[0,13.5]],"o":[[-9.952,0],[0,13.5],[0,0],[0,-9.951]],"v":[[0,-25.715],[-18,-7.715],[0,25.715],[18,-7.715]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.25,25.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":28,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Location bottom Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[13.432,6.364,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"t":28,"s":[20,20,100]},{"t":41,"s":[54,54,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":28,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Pins_Blue","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.201,-1.878],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.4,206.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.555,165.221],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[49.115,205.645],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[105.514,213.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.878],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.878],[0,-2.055]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[171.789,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[368.126,111.436],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[281.531,116.994],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[312.438,125.251],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 23","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.616000007181,0.964999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[329.856,217.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Pins_Orange","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[27.703,137.9],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.744],[0,4.128],[3.303,-0.744]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[381.288,270.794],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[368.126,210.482],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.057],[-2.201,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.2,-1.878],[0,-2.057]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[350.754,184.962],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 27","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[183.575,115.565],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey300","cl":"grey300","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[65.394,-87.762],[21.956,-25.29],[-65.394,87.762]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[77.34,207.456],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-29.617,35.822],[12.473,-5.331],[29.618,-35.821]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[199.566,278.889],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.219,9.823],[8.218,-9.823]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[201.146,227.853],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-33.868,-25.038],[8.293,9.519],[33.867,25.038]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[209.364,226.554],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[19.167,15.54],[-19.167,-15.54]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[119.548,75.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-72.23,5.545],[32.282,-5.545],[55.773,-5.545],[72.23,5.545]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[387.97,276.339],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.098,-157.973],[9.854,-43.904],[-9.854,53.949],[-8.736,83.862],[-3.121,157.973]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[393.327,169.155],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-82.146,-18.015],[-17.286,18.015],[82.146,18.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[364.737,26.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[142.734,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[108.816,73.718],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[105.449,70.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-61.089,79.79],[-10.563,22.215],[30.571,-28.773],[46.604,-61.512],[61.089,-79.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[197.779,251.592],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-181.176,-130.71],[-103.059,-67.44],[-30.743,-4.641],[-18.758,22.842],[23.397,30.81],[65.134,54.857],[89.912,66.865],[181.176,130.71]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[252.512,144.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-197.801,-135.93],[-169.578,-81.272],[-25.091,29.988],[22.053,51.34],[69.798,92.618],[93.582,100.74],[139.02,95.547],[197.801,135.931]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.942,149.433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-43.312,-42.235],[22.43,13.494],[43.312,42.235]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[45.812,287.214],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[18.949,23.783],[-18.949,-23.783]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[109.621,305.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-161.26,59.553],[-73.09,134.19],[-18.949,92.406],[161.26,-134.191]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.76,147.693],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-170.929,-3.155],[-48.146,106.479],[7.347,155.978],[56.846,100.678],[93.971,24.495],[114.081,-34.03],[140.8,-83.029],[163.001,-98.674],[170.928,-155.978]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[176.522,171.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":"Group 35","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-128.003,-94.842],[128.003,94.842]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.569,230.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-143.858,-121.852],[-139.604,-101.518],[143.858,121.852]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.948,211.078],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-101.126,-61.295],[6.382,43.506],[25.329,50.08],[48.533,50.08],[101.126,61.295]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[339.523,72.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[75.549,33.199],[0.527,16.447],[-24.997,16.447],[-75.549,-33.199]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[358.139,131.247],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 39","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.267,11.975],[-22.236,-5.401],[-51.24,-5.401],[-56.267,-11.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[381.289,156.961],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-23.396,-110.585],[23.396,-69.624],[14.115,-1.562],[14.115,110.585]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.625,214.61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.301,-106.862],[-115.301,-91.911],[68.768,50.786],[98.546,63.161],[152.3,106.862]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.44,175.022],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[137.091,-155.846],[25.716,1.527],[6.767,17.403],[-27.65,61.101],[-76.956,108.281],[-137.091,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[205.333,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-56.46,-44.426],[56.46,44.426]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[221.768,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-157.626,-125.059],[157.625,125.059]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[276.914,138.562],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 46","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[128.583,-155.739],[-82.951,117.438],[-128.583,155.739]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[173.622,164.445],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[123.363,-155.846],[-123.363,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[149.452,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 48","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.688,-154.492],[-119.689,154.493]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.376,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.165,-121.947],[-94.165,121.948]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[102.853,137.12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[7.927,-159.713],[-0.193,-105.055],[-1.741,54.527],[-7.927,159.714]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.164,173.216],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-10.143,-7.085],[10.143,7.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[89.154,175.08],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":34,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey700","cl":"grey700","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.871,-11.834],[12.775,-0.847],[2.691,11.834],[-12.775,-0.336]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[260.038,79.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-35.787,-24.613],[-37.505,-22.753],[-34.63,-21.167],[-31.031,-23.281],[-9.88,-6.238],[-12.139,-3.2],[24.52,24.613],[37.505,7.844],[1.732,-21.035],[3.318,-23.281],[1.924,-24.613]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[231.647,43.549],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[37.533,-30.467],[28.951,-30.467],[20.804,-26.143],[14.324,-20.586],[4.618,-16.81],[1.118,-16.23],[-6.173,-7.064],[-37.533,17.139],[-22.607,30.467],[8.069,1.113],[26.699,-16.712]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.376470588235,0.388235294118,0.407843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[150.984,247.163],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey800","cl":"grey800","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,150],[206,150],[206,-150],[-206,-150]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.234999997008,0.250999989229,0.263000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.851,168.936],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 58","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Exact Light Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64.082,57.55,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,0],"ix":1,"l":2},"s":{"a":0,"k":[48.646,48.646,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.34,0.34],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[300,300]},{"t":25,"s":[294,294]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.552941203117,0.670588254929,0.921568632126,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":25,"s":[6]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,149,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[240,240,100]},{"t":45,"s":[306,306,100]}],"ix":6,"l":2}},"ao":0,"sw":117,"sh":98,"sc":"#a77b57","ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"CircleMatte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Exact_ON_DT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/PermissionController/res/raw/coarse_loc_off.json b/PermissionController/res/raw/coarse_loc_off.json new file mode 100644 index 000000000..52927c852 --- /dev/null +++ b/PermissionController/res/raw/coarse_loc_off.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Approximate_OFF_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Approximate_OFF_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[62,62,100]},{"t":60,"s":[90,90,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Highway_Signs","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,67.277],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.48,-0.218],[-0.001,3.653],[5.479,-0.218],[5.999,-3.653],[-6,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.583,71.205],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,65.956],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,70.159],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.218],[0.001,3.653],[5.48,-0.218],[5.999,-3.653],[-5.999,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,74.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,68.837],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,222.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.217],[-0.001,3.653],[5.48,-0.217],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,226.283],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.038],[0,0],[0.292,-1.238],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.238]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,221.035],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.177]],"o":[[-0.042,0.177],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.276],[-6,0.275],[6,0.275],[5.894,-0.276]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,186.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.192],[0,0],[-1.626,3.1],[0.158,1.028],[0,0]],"o":[[1.626,3.1],[0,0],[0.626,-1.192],[0,0],[-0.158,1.028]],"v":[[-5.479,-0.218],[-0.001,3.652],[5.48,-0.218],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.01,190.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.036],[0,0],[1.358,0.038],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.036],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,185.218],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".yellow500","cl":"yellow500","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10.273,-62.792],[10.273,-19.932],[5.308,43.982],[3.529,62.792]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.691,67.791],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0]],"v":[[-69.845,50.02],[-43.615,20.333],[-43.615,-21.96],[-34.261,-28.874],[-24.095,-28.874],[-16.368,-34.974],[-1.728,-31.72],[1.931,-37.821],[7.218,-34.567],[23.079,-42.293],[55.331,-42.293],[69.845,-50.02]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[373.948,242.314],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-113.972,-119.335],[-113.972,-89.146],[-110.288,-76.336],[-118.014,-57.426],[-110.288,-39.939],[-93.615,-15.54],[-81.414,-1.306],[-84.668,17.808],[-75.721,29.193],[-63.521,36.921],[-49.288,39.36],[-41.968,48.714],[-28.954,48.714],[-17.568,53.594],[-2.115,58.067],[5.205,65.794],[21.066,68.233],[51.972,97.107],[62.545,99.954],[75.559,99.954],[118.014,119.335]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.421,175.3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-214.415,-10.167],[-193.269,2.034],[-180.255,2.034],[-172.122,4.88],[-161.548,-0.406],[-149.349,0.001],[-132.675,-7.32],[-126.575,-5.693],[-113.969,-10.167],[-96.075,-8.133],[-88.755,-2.643],[-80.215,-2.643],[-43.208,-2.643],[-22.062,-6.1],[-13.319,0.407],[-0.61,2.034],[17.384,-2.643],[35.278,-0.406],[50.731,2.034],[53.985,6.913],[73.912,4.88],[86.111,10.167],[114.578,10.167],[150.365,10.167],[159.311,4.88],[214.415,2.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[223.889,101.607],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.296,-34.933],[-152.296,-17.527],[-140.503,-1.668],[-129.523,3.62],[-129.523,10.94],[-120.577,15.413],[-117.729,22.327],[-88.449,34.933],[-76.251,30.867],[-68.117,23.953],[-38.43,27.207],[-25.417,28.833],[-7.93,24.767],[20.943,25.986],[31.924,29.24],[38.837,25.58],[51.443,25.58],[60.391,21.513],[86.01,18.667],[97.804,26.8],[152.297,26.393]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.103,105.308],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[21.757,-76.408],[21.757,-63.89],[13.214,-63.89],[-14.031,-38.677],[-19.724,-23.223],[-21.757,54.857],[-18.098,63.803],[-21.757,76.409]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,177.698],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-67.1,-42.533],[-37.413,-29.478],[-34.567,-20.938],[-28.873,-24.191],[-17.08,-23.378],[-5.693,-7.518],[12.2,-3.046],[18.3,5.495],[24.807,9.562],[38.227,11.594],[52.867,19.729],[62.525,21.355],[67.1,29.896],[67.1,42.533]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[160.753,59.919],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-139.893,-158.396],[-135.827,-143.349],[-95.566,-100.243],[-75.233,-93.736],[-51.646,-67.303],[-48.8,-54.29],[-35.38,-42.496],[-32.94,-22.57],[-24.807,-18.91],[-10.574,-14.844],[8.947,16.47],[58.56,53.477],[82.147,84.79],[102.074,96.99],[125.66,133.997],[139.893,142.536],[139.893,158.396]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.307,169.317],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey500","cl":"grey500","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[31.717,-1.007],[-31.717,1.007]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[287.883,119.11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-35.365,-14.212],[-27.159,-14.715],[-22.239,-18.741],[-8.414,-18.741],[6.556,-19.629],[15.02,-18.202],[28.069,-14.212],[28.069,-5.406],[28.069,1.64],[28.069,10.949],[29.704,14.22],[35.239,16.731],[35.365,19.629]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[291.531,123.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.561,-23.371],[-37.444,-22.113],[-31.154,-25.384],[-26.499,-26.767],[-21.215,-28.151],[-18.196,-31.05],[-12.41,-31.528],[-8.761,-31.05],[-7.628,-28.906],[9.733,-22.113],[23.067,-5.129],[29.106,-0.14],[37.283,12.608],[40.931,21.004],[41.561,31.528]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[348.112,171.29],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[4.64,-38.155],[4.64,-8.29],[7.771,-2.101],[6.974,5.21],[2.462,11.432],[-0.966,22.989],[-7.771,38.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[251.527,139.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-19.621,11.458],[-23.788,-11.458],[23.788,-11.458]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[259.299,233.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-36.573,-3.452],[3.542,-3.452],[8.492,-1.858],[25.051,-3.452],[31.026,-2.655],[35.293,0.133],[36.573,2.352],[34.013,2.352],[32.887,3.451]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[297.973,269.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-25.691,-18.223],[-10.328,-18.734],[-5.207,-17.88],[1.281,-18.307],[18.009,-19.077],[25.691,-12.504],[22.789,-10.797],[14.168,0.47],[12.376,5.846],[8.45,13.615],[4.011,19.077]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[307.575,287.115],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.742,46.448],[51.669,43.468],[50.742,37.157],[55.148,32.302],[72.276,31.185],[72.276,5.64],[70.623,-1.526],[70.623,-40.523],[69.383,-42.865],[62.906,-43.554],[56.843,-42.865],[54.224,-44.381],[49.264,-44.381],[45.13,-46.448],[-72.276,-46.448]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[226.967,241.72],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-119.509,-44.611],[-97.824,-40.96],[-89.824,-34.688],[-72.741,-36.266],[-66.754,-33.154],[40.537,-34.971],[59.633,-38.882],[68.047,-38.882],[73.387,-37.306],[74.626,-33.154],[77.919,-31.804],[77.919,-26.232],[80.508,-24.928],[89.57,-21.215],[97.823,-16.038],[105.684,-14.257],[108.827,-8.431],[113.844,5.663],[119.508,8.884],[117.654,19.078],[110.284,25.092],[107.048,37.527],[104.562,44.612]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[278.637,267.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-51.352,2.509],[13.052,3.155],[35.386,-3.155],[51.352,-3.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.911,213.904],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[275.481,202.819],[275.481,222.082]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 39","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.805,-24.131],[-0.446,-14.804],[1.18,18.209],[-2.805,24.131]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[269.292,219.403],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-16.325,-7.523],[-4.713,-0.839],[5.806,-0.839],[16.325,7.523]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[261.745,182.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-52.188,-19.67],[-41.346,-24.51],[-9.628,-12.712],[5.098,-9.312],[20.956,-3.564],[26.135,-4.62],[34.719,-0.671],[41.671,4.326],[52.189,7.729],[46.039,15.452],[46.202,24.509]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[213.259,163.422],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[73.548,-6.19],[75.996,7.565],[69.525,24.299],[59.976,26.175],[53.504,28.441],[47.03,33.188],[47.03,39.488],[34.894,32.698],[26.767,21.986],[17.423,17.347],[-1.49,12.605],[-28.757,3.71],[-37.055,-16.252],[-66.354,-16.252],[-66.354,-18.782],[-69.801,-23.38],[-68.077,-26.092],[-70.654,-30.31],[-74.309,-30.31],[-75.996,-39.488]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[230.554,138.453],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-68.814,-67.689],[-33.186,-67.127],[-13.566,-64.287],[9.77,-52.647],[17.081,-46.04],[58.129,-44.971],[60.347,-29.873],[60.347,-12.806],[61.785,6.876],[59.395,9.068],[60.347,54.334],[65.44,54.334],[68.814,58.411],[63.612,63.472],[65.581,64.877],[63.612,67.689]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.756,235.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-6.636,-10.36],[-2.642,-10.95],[4.106,-13.762],[4.478,-12.242],[5.933,-6.307],[6.636,13.761]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[170.836,181.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[150.903,178.874],[176.77,178.874]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 46","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-46.11,-24.495],[-24.601,-24.495],[-18.978,-22.285],[10.012,-24.495],[26.288,-21.262],[39.924,-19.433],[46.11,-14.795],[41.028,24.495]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[194.482,197.586],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[163.403,74.086],[163.403,98.964]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 48","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.298,-13.017],[-2.298,-7.952],[0.038,-1.57],[0.038,13.017]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.776,74.953],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-25.094,-4.83],[-14.691,-5.955],[-13.004,-4.127],[-6.116,-5.041],[25.094,-5.041],[25.094,5.955]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[113.72,93.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-21.799,-26.171],[-21.799,13.635],[-20.774,20.008],[16.176,26.171],[21.799,24.503]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[143.443,79.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 51","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[88.626,74.857],[151.806,74.857]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 52","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[93.653,52.401],[93.653,94.366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 53","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.723,-35.415],[-1.723,-14.469],[0.62,-12.953],[1.723,35.415]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[162.477,159.858],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-11.575,3.259],[-0.551,-3.259],[4.821,-3.259],[7.166,-1.191],[9.784,-1.191],[11.575,-3.259]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[155.932,166.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-4.489,-23.839],[-6.05,-13.917],[-7.841,-12.401],[-7.841,1.329],[-11.562,10.887],[-11.562,23.84],[11.562,23.84]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[179.069,138.912],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green100","cl":"green100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-84.172,-14.163],[-87.624,-10.786],[-87.624,-7.25],[-91.245,-3.685],[-94.866,-5.154],[-98.486,-11.792],[-101.705,-10.384],[-105.729,-11.792],[-110.154,-14.163],[-116.994,-11.792],[-121.218,-5.355],[-119.247,-2.337],[-117.598,-2.337],[-116.19,1.083],[-110.959,2.286],[-107.137,3.899],[-97.49,3.899],[-97.49,10.725],[-88.428,11.544],[-86.819,19.59],[-68.714,22.205],[-62.64,26.027],[-55.235,33.068],[-45.78,31.79],[-35.723,31.79],[-22.848,31.79],[-17.818,34.678],[-3.957,38.5],[5.315,37.695],[13.563,34.678],[23.622,33.873],[46.757,36.891],[58.826,36.891],[67.678,33.068],[75.14,30.654],[87.996,32.666],[90.209,31.79],[108.313,29.649],[117.768,25.022],[130.243,25.424],[144.523,25.022],[148.34,20.596],[149.955,15.969],[148.34,10.725],[127.193,-42.264],[-25.865,-48.73],[-69.318,-44.784],[-72.134,-37.519],[-82.394,-31.305]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.06,59.651],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 57","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-53.521,-13.307],[-41.048,-20.863],[-32.801,-18.738],[-26.967,-20.863],[-15.902,-15.576],[-11.075,-15.576],[-11.075,-13.307],[4.818,-13.307],[6.83,-15.576],[17.29,-15.576],[20.308,-23.708],[24.532,-22.041],[35.194,-20.863],[45.454,-19.945],[47.264,-15.576],[52.695,-6.064],[46.459,23.708],[42.881,20.101],[34.646,17.883],[-4.626,17.883],[-13.177,20.101],[-23.946,20.101],[-30.387,15.26],[-38.835,16.264],[-49.296,17.271],[-56.009,14.456]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.974,127.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 61","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey100","cl":"grey100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.851,0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[2.851,-0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-209.239,-23.701],[-197.204,-12.299],[-193.403,-15.149],[-193.403,-18.95],[-181.051,-21.801],[-177.251,-23.701],[-166.482,-23.701],[-157.931,-25.918],[-154.447,-25.918],[-135.761,-25.918],[-131.327,-25.918],[-124.36,-25.918],[-118.658,-25.918],[-110.424,-23.701],[-104.406,-19.108],[-98.389,-12.299],[-88.254,0.49],[-84.77,0],[-84.77,-6.282],[-81.92,-1.531],[-83.351,0],[-84.736,0.49],[-86.024,1.81],[-86.644,2.239],[-81.92,12.262],[-74.427,29.3],[-73.52,32.545],[-69.607,41.517],[-69.607,44.142],[-68.7,45.383],[-66.791,56.121],[-66.791,60.226],[-68.748,61.371],[-68.748,62.564],[-70.322,63.995],[-70.37,65.523],[-72.327,67.146],[-73.615,67.146],[-74.14,68.195],[-75.477,68.195],[-75.477,69.389],[-76.908,70.439],[-77.481,71.059],[-77.481,72.014],[-78.293,72.872],[-76.813,73.445],[-76.67,74.877],[-75.572,75.402],[-75.668,77.788],[-74.904,77.788],[-74.904,81.367],[-74.093,81.367],[-73.854,83.611],[-72.947,84.231],[-72.947,85.52],[-71.754,86.331],[-69.655,86.331],[-64.643,86.331],[-64.023,85.472],[-61.302,85.902],[-61.064,86.331],[-57.485,86.665],[-56.005,88.287],[-56.005,89.815],[-54.287,90.912],[-53.428,90.244],[-52.998,90.912],[-51.614,91.199],[-51.614,92.249],[-49.133,92.249],[-48.751,93.299],[-46.651,93.299],[-45.935,94.253],[-45.076,94.253],[-42.833,96.688],[-40.256,96.688],[-40.256,97.498],[-38.108,97.498],[-37.678,98.501],[-35.817,98.501],[-34.815,99.169],[-34.004,99.169],[-33.622,98.453],[-31.14,98.453],[-31.427,97.642],[-31.904,96.782],[-33.431,96.735],[-33.622,91.58],[-33.097,91.58],[-32.763,92.583],[-31.713,92.44],[-31.14,92.965],[-29.279,92.869],[-29.279,91.676],[-30.138,91.342],[-30.043,89.385],[-30.902,88.669],[-30.949,87.237],[-31.904,86.808],[-30.997,86.092],[-30.854,84.66],[-30.138,84.374],[-30.091,80.556],[-31.045,79.984],[-30.759,76.691],[-29.47,78.313],[-29.279,79.268],[-27.37,80.222],[-26.559,79.888],[-25.89,78.934],[-25.604,77.884],[-24.459,76.357],[-21.882,75.402],[-17.586,74.973],[-14.771,74.734],[-14.771,75.831],[-22.072,77.74],[-23.361,78.122],[-24.268,78.6],[-25.127,79.124],[-25.509,80.174],[-27.036,81.272],[-28.181,81.749],[-29.088,83.229],[-29.088,85.997],[-28.849,87.953],[-27.752,88.287],[-26.177,89.385],[-24.173,88.097],[-23.648,88.097],[-22.502,87.143],[-22.74,86.331],[-23.027,83.372],[-22.12,83.133],[-21.548,83.897],[-21.5,84.518],[-20.88,84.66],[-20.88,85.902],[-20.02,86.188],[-19.734,85.328],[-16.918,85.568],[-16.966,84.708],[-19.114,82.895],[-19.591,82.035],[-19.495,81.272],[-16.441,81.415],[-16.107,80.509],[-13.72,80.509],[-12.862,79.076],[-11.239,79.984],[-10.809,83.849],[-9.473,83.897],[-8.996,84.566],[-6.848,84.852],[-6.085,85.949],[-3.364,86.236],[-3.078,84.756],[-3.698,81.701],[-6.037,79.554],[-6.323,78.504],[-3.603,78.456],[-2.219,78.027],[-0.406,77.359],[0.549,77.74],[2.84,77.645],[4.94,77.645],[6.133,78.027],[8.567,78.6],[9.14,79.458],[11.096,79.458],[11.43,80.413],[13.149,80.413],[13.435,80.986],[14.628,81.033],[14.628,80.413],[15.868,79.936],[15.868,78.934],[16.346,81.272],[18.064,81.32],[18.064,80.222],[17.539,80.174],[17.539,78.074],[18.446,78.552],[19.973,80.031],[21.452,80.079],[21.452,82.704],[20.976,83.42],[20.976,84.518],[19.782,84.804],[19.83,85.71],[22.026,86.188],[22.884,86.999],[24.698,87.047],[25.414,87.953],[27.275,87.953],[30.855,87.953],[31.523,86.999],[32.764,87.047],[32.286,88.335],[31.761,88.956],[32.048,90.436],[36.915,90.818],[37.583,98.787],[39.97,102.366],[42.738,103.368],[44.552,106.185],[54.431,117.018],[58.582,120.216],[67.889,127.518],[73.711,130.429],[85.165,134.294],[88.458,136.013],[91.179,136.013],[97.717,142.026],[98.338,144.89],[104.494,147.849],[107.262,150],[211.975,150],[211.975,-151.69],[-210.962,-153.764]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.952941176471,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,171.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 62","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,148.684],[206,148.684],[206,-148.684],[-206,-148.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,172.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 63","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.66,0.66],"y":[0,0]},"t":0,"s":[294,294]},{"t":10,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.66],"y":[0]},"t":0,"s":[6]},{"t":10,"s":[0]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue900","cl":"blue900","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"t":20,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[64.361,64.36,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[150,150,100]},{"t":45,"s":[48.034,48.034,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.829000016755,0.885999971278,0.955999995213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[100]},{"t":32,"s":[70]}],"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[50]},{"t":32,"s":[20]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"CircleMatte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Approximate_OFF_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[{"tm":91,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/PermissionController/res/raw/coarse_loc_on.json b/PermissionController/res/raw/coarse_loc_on.json new file mode 100644 index 000000000..2be289c67 --- /dev/null +++ b/PermissionController/res/raw/coarse_loc_on.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Approximate_ON_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Approximate_ON_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[90,90,100]},{"t":60,"s":[62,62,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Highway_Signs","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,67.277],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.48,-0.218],[-0.001,3.653],[5.479,-0.218],[5.999,-3.653],[-6,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.583,71.205],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[130.584,65.956],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.19],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.19],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,70.159],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.218],[0.001,3.653],[5.48,-0.218],[5.999,-3.653],[-5.999,-3.653]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,74.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.037],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.037],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.129],[0,-1.046],[-2.496,0.129],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[195.904,68.837],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.176]],"o":[[-0.042,0.176],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.275],[-6,0.275],[6,0.275],[5.894,-0.275]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,222.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.193],[0,0],[-1.626,3.099],[0.158,1.029],[0,0]],"o":[[1.626,3.099],[0,0],[0.626,-1.193],[0,0],[-0.158,1.029]],"v":[[-5.479,-0.217],[-0.001,3.653],[5.48,-0.217],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,226.283],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.037],[0,0],[1.358,0.038],[0,0],[0.292,-1.238],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.037],[0,0],[0,0],[-0.292,-1.238]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,221.035],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.029,-0.191],[0,0],[0.041,0.177]],"o":[[-0.042,0.177],[0,0],[-0.03,-0.191],[0,0]],"v":[[-5.894,-0.276],[-6,0.275],[6,0.275],[5.894,-0.276]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.910000011968,0.941000007181,0.995999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,186.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.626,-1.192],[0,0],[-1.626,3.1],[0.158,1.028],[0,0]],"o":[[1.626,3.1],[0,0],[0.626,-1.192],[0,0],[-0.158,1.028]],"v":[[-5.479,-0.218],[-0.001,3.652],[5.48,-0.218],[5.999,-3.652],[-5.999,-3.652]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.258999992819,0.522000002394,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.01,190.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.321,-0.036],[0,0],[1.358,0.038],[0,0],[0.292,-1.237],[0,0]],"o":[[0,0],[-1.358,0.038],[0,0],[-1.321,-0.036],[0,0],[0,0],[-0.292,-1.237]],"v":[[5.065,-1.046],[2.496,0.128],[0,-1.046],[-2.496,0.128],[-5.065,-1.046],[-5.894,1.046],[5.894,1.046]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.917999985639,0.263000009574,0.20800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190.011,185.218],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".yellow500","cl":"yellow500","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10.273,-62.792],[10.273,-19.932],[5.308,43.982],[3.529,62.792]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.691,67.791],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0]],"v":[[-69.845,50.02],[-43.615,20.333],[-43.615,-21.96],[-34.261,-28.874],[-24.095,-28.874],[-16.368,-34.974],[-1.728,-31.72],[1.931,-37.821],[7.218,-34.567],[23.079,-42.293],[55.331,-42.293],[69.845,-50.02]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[373.948,242.314],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-113.972,-119.335],[-113.972,-89.146],[-110.288,-76.336],[-118.014,-57.426],[-110.288,-39.939],[-93.615,-15.54],[-81.414,-1.306],[-84.668,17.808],[-75.721,29.193],[-63.521,36.921],[-49.288,39.36],[-41.968,48.714],[-28.954,48.714],[-17.568,53.594],[-2.115,58.067],[5.205,65.794],[21.066,68.233],[51.972,97.107],[62.545,99.954],[75.559,99.954],[118.014,119.335]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.421,175.3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-214.415,-10.167],[-193.269,2.034],[-180.255,2.034],[-172.122,4.88],[-161.548,-0.406],[-149.349,0.001],[-132.675,-7.32],[-126.575,-5.693],[-113.969,-10.167],[-96.075,-8.133],[-88.755,-2.643],[-80.215,-2.643],[-43.208,-2.643],[-22.062,-6.1],[-13.319,0.407],[-0.61,2.034],[17.384,-2.643],[35.278,-0.406],[50.731,2.034],[53.985,6.913],[73.912,4.88],[86.111,10.167],[114.578,10.167],[150.365,10.167],[159.311,4.88],[214.415,2.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[223.889,101.607],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.296,-34.933],[-152.296,-17.527],[-140.503,-1.668],[-129.523,3.62],[-129.523,10.94],[-120.577,15.413],[-117.729,22.327],[-88.449,34.933],[-76.251,30.867],[-68.117,23.953],[-38.43,27.207],[-25.417,28.833],[-7.93,24.767],[20.943,25.986],[31.924,29.24],[38.837,25.58],[51.443,25.58],[60.391,21.513],[86.01,18.667],[97.804,26.8],[152.297,26.393]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.103,105.308],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[21.757,-76.408],[21.757,-63.89],[13.214,-63.89],[-14.031,-38.677],[-19.724,-23.223],[-21.757,54.857],[-18.098,63.803],[-21.757,76.409]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[210.57,177.698],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-67.1,-42.533],[-37.413,-29.478],[-34.567,-20.938],[-28.873,-24.191],[-17.08,-23.378],[-5.693,-7.518],[12.2,-3.046],[18.3,5.495],[24.807,9.562],[38.227,11.594],[52.867,19.729],[62.525,21.355],[67.1,29.896],[67.1,42.533]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[160.753,59.919],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-139.893,-158.396],[-135.827,-143.349],[-95.566,-100.243],[-75.233,-93.736],[-51.646,-67.303],[-48.8,-54.29],[-35.38,-42.496],[-32.94,-22.57],[-24.807,-18.91],[-10.574,-14.844],[8.947,16.47],[58.56,53.477],[82.147,84.79],[102.074,96.99],[125.66,133.997],[139.893,142.536],[139.893,158.396]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.737254901961,0.01568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.307,169.317],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey500","cl":"grey500","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[31.717,-1.007],[-31.717,1.007]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[287.883,119.11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-35.365,-14.212],[-27.159,-14.715],[-22.239,-18.741],[-8.414,-18.741],[6.556,-19.629],[15.02,-18.202],[28.069,-14.212],[28.069,-5.406],[28.069,1.64],[28.069,10.949],[29.704,14.22],[35.239,16.731],[35.365,19.629]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[291.531,123.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-41.561,-23.371],[-37.444,-22.113],[-31.154,-25.384],[-26.499,-26.767],[-21.215,-28.151],[-18.196,-31.05],[-12.41,-31.528],[-8.761,-31.05],[-7.628,-28.906],[9.733,-22.113],[23.067,-5.129],[29.106,-0.14],[37.283,12.608],[40.931,21.004],[41.561,31.528]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[348.112,171.29],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[4.64,-38.155],[4.64,-8.29],[7.771,-2.101],[6.974,5.21],[2.462,11.432],[-0.966,22.989],[-7.771,38.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[251.527,139.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-19.621,11.458],[-23.788,-11.458],[23.788,-11.458]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[259.299,233.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-36.573,-3.452],[3.542,-3.452],[8.492,-1.858],[25.051,-3.452],[31.026,-2.655],[35.293,0.133],[36.573,2.352],[34.013,2.352],[32.887,3.451]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[297.973,269.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-25.691,-18.223],[-10.328,-18.734],[-5.207,-17.88],[1.281,-18.307],[18.009,-19.077],[25.691,-12.504],[22.789,-10.797],[14.168,0.47],[12.376,5.846],[8.45,13.615],[4.011,19.077]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[307.575,287.115],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.742,46.448],[51.669,43.468],[50.742,37.157],[55.148,32.302],[72.276,31.185],[72.276,5.64],[70.623,-1.526],[70.623,-40.523],[69.383,-42.865],[62.906,-43.554],[56.843,-42.865],[54.224,-44.381],[49.264,-44.381],[45.13,-46.448],[-72.276,-46.448]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[226.967,241.72],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-119.509,-44.611],[-97.824,-40.96],[-89.824,-34.688],[-72.741,-36.266],[-66.754,-33.154],[40.537,-34.971],[59.633,-38.882],[68.047,-38.882],[73.387,-37.306],[74.626,-33.154],[77.919,-31.804],[77.919,-26.232],[80.508,-24.928],[89.57,-21.215],[97.823,-16.038],[105.684,-14.257],[108.827,-8.431],[113.844,5.663],[119.508,8.884],[117.654,19.078],[110.284,25.092],[107.048,37.527],[104.562,44.612]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[278.637,267.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-51.352,2.509],[13.052,3.155],[35.386,-3.155],[51.352,-3.155]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.911,213.904],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[275.481,202.819],[275.481,222.082]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 39","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.805,-24.131],[-0.446,-14.804],[1.18,18.209],[-2.805,24.131]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[269.292,219.403],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-16.325,-7.523],[-4.713,-0.839],[5.806,-0.839],[16.325,7.523]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[261.745,182.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-52.188,-19.67],[-41.346,-24.51],[-9.628,-12.712],[5.098,-9.312],[20.956,-3.564],[26.135,-4.62],[34.719,-0.671],[41.671,4.326],[52.189,7.729],[46.039,15.452],[46.202,24.509]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[213.259,163.422],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[73.548,-6.19],[75.996,7.565],[69.525,24.299],[59.976,26.175],[53.504,28.441],[47.03,33.188],[47.03,39.488],[34.894,32.698],[26.767,21.986],[17.423,17.347],[-1.49,12.605],[-28.757,3.71],[-37.055,-16.252],[-66.354,-16.252],[-66.354,-18.782],[-69.801,-23.38],[-68.077,-26.092],[-70.654,-30.31],[-74.309,-30.31],[-75.996,-39.488]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[230.554,138.453],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-68.814,-67.689],[-33.186,-67.127],[-13.566,-64.287],[9.77,-52.647],[17.081,-46.04],[58.129,-44.971],[60.347,-29.873],[60.347,-12.806],[61.785,6.876],[59.395,9.068],[60.347,54.334],[65.44,54.334],[68.814,58.411],[63.612,63.472],[65.581,64.877],[63.612,67.689]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[243.756,235.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-6.636,-10.36],[-2.642,-10.95],[4.106,-13.762],[4.478,-12.242],[5.933,-6.307],[6.636,13.761]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[170.836,181.51],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[150.903,178.874],[176.77,178.874]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 46","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-46.11,-24.495],[-24.601,-24.495],[-18.978,-22.285],[10.012,-24.495],[26.288,-21.262],[39.924,-19.433],[46.11,-14.795],[41.028,24.495]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[194.482,197.586],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[163.403,74.086],[163.403,98.964]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 48","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[2.298,-13.017],[-2.298,-7.952],[0.038,-1.57],[0.038,13.017]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.776,74.953],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-25.094,-4.83],[-14.691,-5.955],[-13.004,-4.127],[-6.116,-5.041],[25.094,-5.041],[25.094,5.955]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[113.72,93.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-21.799,-26.171],[-21.799,13.635],[-20.774,20.008],[16.176,26.171],[21.799,24.503]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[143.443,79.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 51","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[88.626,74.857],[151.806,74.857]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 52","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[93.653,52.401],[93.653,94.366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":"Group 53","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.723,-35.415],[-1.723,-14.469],[0.62,-12.953],[1.723,35.415]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[162.477,159.858],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-11.575,3.259],[-0.551,-3.259],[4.821,-3.259],[7.166,-1.191],[9.784,-1.191],[11.575,-3.259]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[155.932,166.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[-4.489,-23.839],[-6.05,-13.917],[-7.841,-12.401],[-7.841,1.329],[-11.562,10.887],[-11.562,23.84],[11.562,23.84]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[179.069,138.912],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-84.172,-14.163],[-87.624,-10.786],[-87.624,-7.25],[-91.245,-3.685],[-94.866,-5.154],[-98.486,-11.792],[-101.705,-10.384],[-105.729,-11.792],[-110.154,-14.163],[-116.994,-11.792],[-121.218,-5.355],[-119.247,-2.337],[-117.598,-2.337],[-116.19,1.083],[-110.959,2.286],[-107.137,3.899],[-97.49,3.899],[-97.49,10.725],[-88.428,11.544],[-86.819,19.59],[-68.714,22.205],[-62.64,26.027],[-55.235,33.068],[-45.78,31.79],[-35.723,31.79],[-22.848,31.79],[-17.818,34.678],[-3.957,38.5],[5.315,37.695],[13.563,34.678],[23.622,33.873],[46.757,36.891],[58.826,36.891],[67.678,33.068],[75.14,30.654],[87.996,32.666],[90.209,31.79],[108.313,29.649],[117.768,25.022],[130.243,25.424],[144.523,25.022],[148.34,20.596],[149.955,15.969],[148.34,10.725],[127.193,-42.264],[-25.865,-48.73],[-69.318,-44.784],[-72.134,-37.519],[-82.394,-31.305]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.06,59.651],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 57","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-53.521,-13.307],[-41.048,-20.863],[-32.801,-18.738],[-26.967,-20.863],[-15.902,-15.576],[-11.075,-15.576],[-11.075,-13.307],[4.818,-13.307],[6.83,-15.576],[17.29,-15.576],[20.308,-23.708],[24.532,-22.041],[35.194,-20.863],[45.454,-19.945],[47.264,-15.576],[52.695,-6.064],[46.459,23.708],[42.881,20.101],[34.646,17.883],[-4.626,17.883],[-13.177,20.101],[-23.946,20.101],[-30.387,15.26],[-38.835,16.264],[-49.296,17.271],[-56.009,14.456]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.974,127.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 61","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey100","cl":"grey100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.851,0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[2.851,-0.95],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-209.239,-23.701],[-197.204,-12.299],[-193.403,-15.149],[-193.403,-18.95],[-181.051,-21.801],[-177.251,-23.701],[-166.482,-23.701],[-157.931,-25.918],[-154.447,-25.918],[-135.761,-25.918],[-131.327,-25.918],[-124.36,-25.918],[-118.658,-25.918],[-110.424,-23.701],[-104.406,-19.108],[-98.389,-12.299],[-88.254,0.49],[-84.77,0],[-84.77,-6.282],[-81.92,-1.531],[-83.351,0],[-84.736,0.49],[-86.024,1.81],[-86.644,2.239],[-81.92,12.262],[-74.427,29.3],[-73.52,32.545],[-69.607,41.517],[-69.607,44.142],[-68.7,45.383],[-66.791,56.121],[-66.791,60.226],[-68.748,61.371],[-68.748,62.564],[-70.322,63.995],[-70.37,65.523],[-72.327,67.146],[-73.615,67.146],[-74.14,68.195],[-75.477,68.195],[-75.477,69.389],[-76.908,70.439],[-77.481,71.059],[-77.481,72.014],[-78.293,72.872],[-76.813,73.445],[-76.67,74.877],[-75.572,75.402],[-75.668,77.788],[-74.904,77.788],[-74.904,81.367],[-74.093,81.367],[-73.854,83.611],[-72.947,84.231],[-72.947,85.52],[-71.754,86.331],[-69.655,86.331],[-64.643,86.331],[-64.023,85.472],[-61.302,85.902],[-61.064,86.331],[-57.485,86.665],[-56.005,88.287],[-56.005,89.815],[-54.287,90.912],[-53.428,90.244],[-52.998,90.912],[-51.614,91.199],[-51.614,92.249],[-49.133,92.249],[-48.751,93.299],[-46.651,93.299],[-45.935,94.253],[-45.076,94.253],[-42.833,96.688],[-40.256,96.688],[-40.256,97.498],[-38.108,97.498],[-37.678,98.501],[-35.817,98.501],[-34.815,99.169],[-34.004,99.169],[-33.622,98.453],[-31.14,98.453],[-31.427,97.642],[-31.904,96.782],[-33.431,96.735],[-33.622,91.58],[-33.097,91.58],[-32.763,92.583],[-31.713,92.44],[-31.14,92.965],[-29.279,92.869],[-29.279,91.676],[-30.138,91.342],[-30.043,89.385],[-30.902,88.669],[-30.949,87.237],[-31.904,86.808],[-30.997,86.092],[-30.854,84.66],[-30.138,84.374],[-30.091,80.556],[-31.045,79.984],[-30.759,76.691],[-29.47,78.313],[-29.279,79.268],[-27.37,80.222],[-26.559,79.888],[-25.89,78.934],[-25.604,77.884],[-24.459,76.357],[-21.882,75.402],[-17.586,74.973],[-14.771,74.734],[-14.771,75.831],[-22.072,77.74],[-23.361,78.122],[-24.268,78.6],[-25.127,79.124],[-25.509,80.174],[-27.036,81.272],[-28.181,81.749],[-29.088,83.229],[-29.088,85.997],[-28.849,87.953],[-27.752,88.287],[-26.177,89.385],[-24.173,88.097],[-23.648,88.097],[-22.502,87.143],[-22.74,86.331],[-23.027,83.372],[-22.12,83.133],[-21.548,83.897],[-21.5,84.518],[-20.88,84.66],[-20.88,85.902],[-20.02,86.188],[-19.734,85.328],[-16.918,85.568],[-16.966,84.708],[-19.114,82.895],[-19.591,82.035],[-19.495,81.272],[-16.441,81.415],[-16.107,80.509],[-13.72,80.509],[-12.862,79.076],[-11.239,79.984],[-10.809,83.849],[-9.473,83.897],[-8.996,84.566],[-6.848,84.852],[-6.085,85.949],[-3.364,86.236],[-3.078,84.756],[-3.698,81.701],[-6.037,79.554],[-6.323,78.504],[-3.603,78.456],[-2.219,78.027],[-0.406,77.359],[0.549,77.74],[2.84,77.645],[4.94,77.645],[6.133,78.027],[8.567,78.6],[9.14,79.458],[11.096,79.458],[11.43,80.413],[13.149,80.413],[13.435,80.986],[14.628,81.033],[14.628,80.413],[15.868,79.936],[15.868,78.934],[16.346,81.272],[18.064,81.32],[18.064,80.222],[17.539,80.174],[17.539,78.074],[18.446,78.552],[19.973,80.031],[21.452,80.079],[21.452,82.704],[20.976,83.42],[20.976,84.518],[19.782,84.804],[19.83,85.71],[22.026,86.188],[22.884,86.999],[24.698,87.047],[25.414,87.953],[27.275,87.953],[30.855,87.953],[31.523,86.999],[32.764,87.047],[32.286,88.335],[31.761,88.956],[32.048,90.436],[36.915,90.818],[37.583,98.787],[39.97,102.366],[42.738,103.368],[44.552,106.185],[54.431,117.018],[58.582,120.216],[67.889,127.518],[73.711,130.429],[85.165,134.294],[88.458,136.013],[91.179,136.013],[97.717,142.026],[98.338,144.89],[104.494,147.849],[107.262,150],[211.975,150],[211.975,-151.69],[-210.962,-153.764]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.952941176471,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,171.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 62","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[57.694,49,0],"ix":2,"l":2},"a":{"a":0,"k":[232.123,166.357,0],"ix":1,"l":2},"s":{"a":0,"k":[79.922,79.922,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,148.684],[206,148.684],[206,-148.684],[-206,-148.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[223.279,172.467],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 63","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.34,0.34],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[300,300]},{"t":25,"s":[294,294]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":25,"s":[6]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Pale Orange Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Blue Circle Outlines","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[64.361,64.36,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[66.714,66.714,100]},{"t":45,"s":[208.333,208.333,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.829000016755,0.885999971278,0.955999995213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[70]},{"t":21,"s":[100]}],"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-34.164],[34.165,0],[0,34.165],[-34.165,0]],"o":[[0,34.165],[-34.165,0],[0,-34.164],[34.165,0]],"v":[[61.861,-0.001],[0,61.861],[-61.861,-0.001],[0,-61.861]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0,0,0,1]},{"t":21,"s":[0.552941203117,0.670588254929,0.921568632126,1]}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[20]},{"t":21,"s":[50]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[64.361,64.361],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"CircleMatte 4","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Approximate_ON_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/PermissionController/res/raw/fine_loc_off.json b/PermissionController/res/raw/fine_loc_off.json new file mode 100644 index 000000000..3aa81bab5 --- /dev/null +++ b/PermissionController/res/raw/fine_loc_off.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Exact_OFF_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Exact_OFF_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[102,102,100]},{"t":60,"s":[80,80,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Location Pointer Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[18.25,51.679,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.967,0.967,0.2],"y":[1,1,1]},"o":{"x":[0.499,0.499,0.4],"y":[0,0,0]},"t":24,"s":[54,54,100]},{"t":42,"s":[10,10,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.551,0],[0,-3.55],[3.55,0],[0,3.551]],"o":[[3.55,0],[0,3.551],[-3.551,0],[0,-3.55]],"v":[[0.001,-6.429],[6.428,-0.001],[0.001,6.429],[-6.428,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","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},{"ty":"tr","p":{"a":0,"k":[18.249,18.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.952,0],[0,-9.951],[0,0],[0,13.5]],"o":[[-9.952,0],[0,13.5],[0,0],[0,-9.951]],"v":[[0,-25.715],[-18,-7.715],[0,25.715],[18,-7.715]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196080506,0.305882364511,0.65098041296,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.25,25.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":43,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Location bottom Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[13.432,6.364,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.967,0.967,0.2],"y":[1,1,1]},"o":{"x":[0.499,0.499,0.4],"y":[0,0,0]},"t":24,"s":[54,54,100]},{"t":42,"s":[10,10,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":43,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue900","cl":"blue900","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.201,-1.878],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.4,206.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.555,165.221],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[49.115,205.645],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[105.514,213.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.878],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.878],[0,-2.055]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[171.789,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[368.126,111.436],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[281.531,116.994],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[312.438,125.251],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 23","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[329.856,217.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Pins_Orange","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[27.703,137.9],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.744],[0,4.128],[3.303,-0.744]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[381.288,270.794],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[368.126,210.482],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.057],[-2.201,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.2,-1.878],[0,-2.057]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[350.754,184.962],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 27","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[183.575,115.565],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey500","cl":"grey500","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[65.394,-87.762],[21.956,-25.29],[-65.394,87.762]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[77.34,207.456],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-29.617,35.822],[12.473,-5.331],[29.618,-35.821]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[199.566,278.889],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.219,9.823],[8.218,-9.823]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[201.146,227.853],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-33.868,-25.038],[8.293,9.519],[33.867,25.038]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[209.364,226.554],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[19.167,15.54],[-19.167,-15.54]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[119.548,75.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-72.23,5.545],[32.282,-5.545],[55.773,-5.545],[72.23,5.545]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[387.97,276.339],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.098,-157.973],[9.854,-43.904],[-9.854,53.949],[-8.736,83.862],[-3.121,157.973]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[393.327,169.155],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-82.146,-18.015],[-17.286,18.015],[82.146,18.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[364.737,26.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[142.734,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[108.816,73.718],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[105.449,70.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-61.089,79.79],[-10.563,22.215],[30.571,-28.773],[46.604,-61.512],[61.089,-79.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[197.779,251.592],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-181.176,-130.71],[-103.059,-67.44],[-30.743,-4.641],[-18.758,22.842],[23.397,30.81],[65.134,54.857],[89.912,66.865],[181.176,130.71]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[252.512,144.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-197.801,-135.93],[-169.578,-81.272],[-25.091,29.988],[22.053,51.34],[69.798,92.618],[93.582,100.74],[139.02,95.547],[197.801,135.931]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.942,149.433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-43.312,-42.235],[22.43,13.494],[43.312,42.235]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[45.812,287.214],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[18.949,23.783],[-18.949,-23.783]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[109.621,305.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-161.26,59.553],[-73.09,134.19],[-18.949,92.406],[161.26,-134.191]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.76,147.693],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-170.929,-3.155],[-48.146,106.479],[7.347,155.978],[56.846,100.678],[93.971,24.495],[114.081,-34.03],[140.8,-83.029],[163.001,-98.674],[170.928,-155.978]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[176.522,171.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":"Group 35","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-128.003,-94.842],[128.003,94.842]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.569,230.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-143.858,-121.852],[-139.604,-101.518],[143.858,121.852]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.948,211.078],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-101.126,-61.295],[6.382,43.506],[25.329,50.08],[48.533,50.08],[101.126,61.295]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[339.523,72.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[75.549,33.199],[0.527,16.447],[-24.997,16.447],[-75.549,-33.199]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[358.139,131.247],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 39","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.267,11.975],[-22.236,-5.401],[-51.24,-5.401],[-56.267,-11.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[381.289,156.961],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-23.396,-110.585],[23.396,-69.624],[14.115,-1.562],[14.115,110.585]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.625,214.61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.301,-106.862],[-115.301,-91.911],[68.768,50.786],[98.546,63.161],[152.3,106.862]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.44,175.022],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[137.091,-155.846],[25.716,1.527],[6.767,17.403],[-27.65,61.101],[-76.956,108.281],[-137.091,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[205.333,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-56.46,-44.426],[56.46,44.426]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[221.768,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-157.626,-125.059],[157.625,125.059]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[276.914,138.562],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 46","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[128.583,-155.739],[-82.951,117.438],[-128.583,155.739]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[173.622,164.445],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[123.363,-155.846],[-123.363,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[149.452,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 48","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.688,-154.492],[-119.689,154.493]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.376,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.165,-121.947],[-94.165,121.948]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[102.853,137.12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green100","cl":"green100","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.871,-11.834],[12.775,-0.847],[2.691,11.834],[-12.775,-0.336]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[260.038,79.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-35.787,-24.613],[-37.505,-22.753],[-34.63,-21.167],[-31.031,-23.281],[-9.88,-6.238],[-12.139,-3.2],[24.52,24.613],[37.505,7.844],[1.732,-21.035],[3.318,-23.281],[1.924,-24.613]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[231.647,43.549],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[37.533,-30.467],[28.951,-30.467],[20.804,-26.143],[14.324,-20.586],[4.618,-16.81],[1.118,-16.23],[-6.173,-7.064],[-37.533,17.139],[-22.607,30.467],[8.069,1.113],[26.699,-16.712]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[150.984,247.163],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey100","cl":"grey100","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,150],[206,150],[206,-150],[-206,-150]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.952941176471,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.851,168.936],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 58","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Exact Light Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64.082,57.55,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,0],"ix":1,"l":2},"s":{"a":0,"k":[48.646,48.646,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.66,0.66],"y":[0,0]},"t":0,"s":[294,294]},{"t":10,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.66],"y":[0]},"t":0,"s":[6]},{"t":10,"s":[0]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,149,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.6],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.8],"y":[0,0,0]},"t":0,"s":[306,306,100]},{"t":45,"s":[240,240,100]}],"ix":6,"l":2}},"ao":0,"sw":117,"sh":98,"sc":"#a77b57","ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"CircleMatte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Exact_OFF_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[{"tm":91,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/PermissionController/res/raw/fine_loc_on.json b/PermissionController/res/raw/fine_loc_on.json new file mode 100644 index 000000000..344942361 --- /dev/null +++ b/PermissionController/res/raw/fine_loc_on.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":91,"w":300,"h":300,"nm":"Location_Accuracy_Exact_ON_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Exact_ON_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.5,49,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":15,"s":[80,80,100]},{"t":60,"s":[102,102,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Location Pointer Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[18.25,51.679,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.552,0.552,0.667],"y":[1,1,1]},"o":{"x":[0.046,0.046,0.333],"y":[0,0,0]},"t":28,"s":[20,20,100]},{"i":{"x":[0.468,0.468,0.667],"y":[1,1,1]},"o":{"x":[0.441,0.441,0.333],"y":[0,0,0]},"t":42,"s":[58,58,100]},{"t":60,"s":[54,54,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.551,0],[0,-3.55],[3.55,0],[0,3.551]],"o":[[3.55,0],[0,3.551],[-3.551,0],[0,-3.55]],"v":[[0.001,-6.429],[6.428,-0.001],[0.001,6.429],[-6.428,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","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},{"ty":"tr","p":{"a":0,"k":[18.249,18.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.952,0],[0,-9.951],[0,0],[0,13.5]],"o":[[-9.952,0],[0,13.5],[0,0],[0,-9.951]],"v":[[0,-25.715],[-18,-7.715],[0,25.715],[18,-7.715]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196080506,0.305882364511,0.65098041296,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.25,25.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":28,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Location bottom Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.298,63.254,0],"ix":2,"l":2},"a":{"a":0,"k":[13.432,6.364,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"t":28,"s":[20,20,100]},{"t":41,"s":[54,54,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.135],[6.037,0],[0,2.134],[-6.038,0]],"o":[[0,2.134],[-6.038,0],[0,-2.135],[6.037,0]],"v":[[10.933,0],[0.001,3.863],[-10.933,0],[0.001,-3.863]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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},{"ty":"tr","p":{"a":0,"k":[13.432,6.364],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":28,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue900","cl":"blue900","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.201,-1.878],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.4,206.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 16","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.555,165.221],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 17","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[49.115,205.645],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 18","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[105.514,213.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.055],[-2.2,-1.878],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.878],[0,-2.055]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[171.789,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 20","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[368.126,111.436],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 21","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[281.531,116.994],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 22","np":4,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.055],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.055]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[312.438,125.251],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 23","np":4,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[329.856,217.762],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":4,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Pins_Orange","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[27.703,137.9],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 15","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.744],[0,4.128],[3.303,-0.744]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[381.288,270.794],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 25","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.2,-1.879],[0,1.371]],"o":[[-1.733,0],[0,1.371],[2.201,-1.879],[0,-2.056]],"v":[[0,-4.128],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[368.126,210.482],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 26","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.733,0],[0,-2.057],[-2.201,-1.878],[0,1.37]],"o":[[-1.734,0],[0,1.37],[2.2,-1.878],[0,-2.057]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.128],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[350.754,184.962],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 27","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.734,0],[0,-2.056],[-2.201,-1.879],[0,1.371]],"o":[[-1.734,0],[0,1.371],[2.2,-1.879],[0,-2.056]],"v":[[0,-4.129],[-3.303,-0.743],[0,4.129],[3.303,-0.743]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.948999980852,0.6,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":[183.575,115.565],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 28","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey500","cl":"grey500","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[65.394,-87.762],[21.956,-25.29],[-65.394,87.762]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[77.34,207.456],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-29.617,35.822],[12.473,-5.331],[29.618,-35.821]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[199.566,278.889],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.219,9.823],[8.218,-9.823]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[201.146,227.853],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-33.868,-25.038],[8.293,9.519],[33.867,25.038]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[209.364,226.554],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[19.167,15.54],[-19.167,-15.54]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[119.548,75.138],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-72.23,5.545],[32.282,-5.545],[55.773,-5.545],[72.23,5.545]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[387.97,276.339],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.098,-157.973],[9.854,-43.904],[-9.854,53.949],[-8.736,83.862],[-3.121,157.973]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[393.327,169.155],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-82.146,-18.015],[-17.286,18.015],[82.146,18.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[364.737,26.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[142.734,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[108.816,73.718],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 13","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.193,59.098],[50.193,-59.098]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[105.449,70.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 14","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-61.089,79.79],[-10.563,22.215],[30.571,-28.773],[46.604,-61.512],[61.089,-79.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[197.779,251.592],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 29","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-181.176,-130.71],[-103.059,-67.44],[-30.743,-4.641],[-18.758,22.842],[23.397,30.81],[65.134,54.857],[89.912,66.865],[181.176,130.71]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[252.512,144.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 30","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"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":[[-197.801,-135.93],[-169.578,-81.272],[-25.091,29.988],[22.053,51.34],[69.798,92.618],[93.582,100.74],[139.02,95.547],[197.801,135.931]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[245.942,149.433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 31","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-43.312,-42.235],[22.43,13.494],[43.312,42.235]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[45.812,287.214],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 32","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[18.949,23.783],[-18.949,-23.783]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[109.621,305.667],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 33","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-161.26,59.553],[-73.09,134.19],[-18.949,92.406],[161.26,-134.191]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.76,147.693],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 34","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[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],[0,0]],"v":[[-170.929,-3.155],[-48.146,106.479],[7.347,155.978],[56.846,100.678],[93.971,24.495],[114.081,-34.03],[140.8,-83.029],[163.001,-98.674],[170.928,-155.978]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[176.522,171.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":"Group 35","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-128.003,-94.842],[128.003,94.842]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.569,230.353],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-143.858,-121.852],[-139.604,-101.518],[143.858,121.852]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.948,211.078],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-101.126,-61.295],[6.382,43.506],[25.329,50.08],[48.533,50.08],[101.126,61.295]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[339.523,72.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 38","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[75.549,33.199],[0.527,16.447],[-24.997,16.447],[-75.549,-33.199]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[358.139,131.247],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 39","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.267,11.975],[-22.236,-5.401],[-51.24,-5.401],[-56.267,-11.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[381.289,156.961],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 40","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-23.396,-110.585],[23.396,-69.624],[14.115,-1.562],[14.115,110.585]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[301.625,214.61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 41","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-152.301,-106.862],[-115.301,-91.911],[68.768,50.786],[98.546,63.161],[152.3,106.862]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[163.44,175.022],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 43","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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]],"v":[[137.091,-155.846],[25.716,1.527],[6.767,17.403],[-27.65,61.101],[-76.956,108.281],[-137.091,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[205.333,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 44","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-56.46,-44.426],[56.46,44.426]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[221.768,59.599],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 45","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-157.626,-125.059],[157.625,125.059]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[276.914,138.562],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 46","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[128.583,-155.739],[-82.951,117.438],[-128.583,155.739]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[173.622,164.445],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 47","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[123.363,-155.846],[-123.363,155.846]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[149.452,169.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 48","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.688,-154.492],[-119.689,154.493]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[128.376,167.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 49","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.165,-121.947],[-94.165,121.948]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[102.853,137.12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 50","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[7.927,-159.713],[-0.193,-105.055],[-1.741,54.527],[-7.927,159.714]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.164,173.216],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 42","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-10.143,-7.085],[10.143,7.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.505882352941,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[89.154,175.08],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":34,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green100","cl":"green100","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.871,-11.834],[12.775,-0.847],[2.691,11.834],[-12.775,-0.336]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[260.038,79.995],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 54","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[-35.787,-24.613],[-37.505,-22.753],[-34.63,-21.167],[-31.031,-23.281],[-9.88,-6.238],[-12.139,-3.2],[24.52,24.613],[37.505,7.844],[1.732,-21.035],[3.318,-23.281],[1.924,-24.613]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[231.647,43.549],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 55","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[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],[0,0],[0,0],[0,0]],"v":[[37.533,-30.467],[28.951,-30.467],[20.804,-26.143],[14.324,-20.586],[4.618,-16.81],[1.118,-16.23],[-6.173,-7.064],[-37.533,17.139],[-22.607,30.467],[8.069,1.113],[26.699,-16.712]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[150.984,247.163],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 56","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey100","cl":"grey100","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[230.884,168.455,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,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":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-206,150],[206,150],[206,-150],[-206,-150]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.952941176471,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.851,168.936],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 58","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Exact Light Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64.082,57.55,0],"ix":2,"l":2},"a":{"a":0,"k":[231.35,167.715,0],"ix":1,"l":2},"s":{"a":0,"k":[48.646,48.646,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[101.086,101.086,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.34,0.34],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[300,300]},{"t":25,"s":[294,294]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":25,"s":[6]}],"ix":5},"lc":1,"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":[98.741,98.741],"ix":3},"r":{"a":0,"k":0,"ix":6},"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 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Scale Controller","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,149,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[240,240,100]},{"t":45,"s":[306,306,100]}],"ix":6,"l":2}},"ao":0,"sw":117,"sh":98,"sc":"#a77b57","ip":0,"op":585,"st":-15,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"CircleMatte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[82.705,0],[0,-82.705],[-82.705,0],[0,82.705]],"o":[[-82.705,0],[0,82.705],[82.705,0],[0,-82.705]],"v":[[-0.125,-149.938],[-149.875,-0.188],[-0.125,149.562],[149.625,-0.188]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":-1,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4]],"v":[[228,150],[-238,150],[-266,122],[-266,-122],[-238,-150],[228,-150],[256,-122],[256,122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Exact_ON_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[58.5,49,0],"ix":1,"l":2},"s":{"a":0,"k":[307.2,307.2,100],"ix":6,"l":2}},"ao":0,"w":117,"h":98,"ip":0,"op":585,"st":-15,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml index 9f4b10a4b..ff64419b2 100644 --- a/PermissionController/res/values-af/strings.xml +++ b/PermissionController/res/values-af/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die program gebruik word\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Hou \"Net hierdie keer\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer inligting"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Gee toegang tot alle foto’s"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Kies foto’s"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Kies meer foto’s"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Moenie meer foto’s kies nie"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Moet steeds nie toelaat nie"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Maak toe"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dag}other{# dae}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 uur}other{# uur}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min.}other{# min.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek.}other{# sek.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dag}other{# dae}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# uur}other{# uur}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min.}other{# min.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek.}other{# sek.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Enige toestemming"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Enige tyd"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Afgelope 7 dae"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Afgelope 24 uur"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Afgelope 1 uur"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Afgelope 15 minute"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Afgelope 1 minuut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Afgelope # dag}other{Afgelope # dae}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Afgelope # uur}other{Afgelope # uur}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Afgelope # minuut}other{Afgelope # minute}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Geen toestemminggebruike nie"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Mees onlangse toegang in enige tydperk"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Mees onlangse toegang in afgelope 7 dae"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Toestemminggebruik in afgelope 1 uur"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Toestemminggebruik in afgelope 15 minute"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Toestemminggebruik in afgelope 1 minuut"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nie in die afgelope 24 uur gebruik nie"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nie in die afgelope 7 dae gebruik nie"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nie in die afgelope # dag gebruik nie}other{Nie in die afgelope # dae gebruik nie}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nie in die afgelope # uur gebruik nie}other{Nie in die afgelope # uur gebruik nie}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Gebruik deur 1 program}other{Gebruik deur # programme}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Sien alles in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Gefiltreer volgens: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Laat net toegang tot media toe"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Laat altyd toe"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Laat net toe terwyl jy program gebruik"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Laat alle foto’s toe"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Laat geselekteerde foto’s toe"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Vra elke keer"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Moenie toelaat nie"</string> <string name="precise_image_description" msgid="6349638632303619872">"Presiese ligging"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nie toegelaat nie"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Sien meer programme wat toegang tot alle lêers het"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dae}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 uur}other{# uur}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuut}other{# minute}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekonde}other{# sekondes}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# uur}other{# uur}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuut}other{# minute}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekonde}other{# sekondes}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Toestemmingonthounotas"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ongebruikte program"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ongebruikte programme"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot <b>foto\'s, video\'s, musiek, oudio en ander lêers</b> op hierdie toestel?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot musiek en oudio op hierdie toestel?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot foto\'s en video\'s op hierdie toestel?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot meer foto’s?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om oudio op te neem?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die program sal net kan oudio opneem terwyl jy die program gebruik"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om oudio op te neem?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Wys ’n boodskap wanneer programme toegang het tot teks, prente of ander inhoud wat jy gekopieer het"</string> <string name="show_password_title" msgid="2877269286984684659">"Wys wagwoorde"</string> <string name="show_password_summary" msgid="1110166488865981610">"Wys karakters kortliks terwyl jy tik"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Hierdie app het verklaar dat dit dalk <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data met derde party sal deel"</string> </resources> diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml index 8bec4bc12..88dc043a0 100644 --- a/PermissionController/res/values-am/strings.xml +++ b/PermissionController/res/values-am/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"«መተግበሪያው በጥቅም ላይ እያለ» አቆይ"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"«አሁን ብቻ»ን አቆይ"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ተጨማሪ መረጃ"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ለማንኛውም አትፍቀድ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"አሰናብት"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ከ<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ቀን}one{# ቀኖች}other{# ቀኖች}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ሰዓት}one{# ሰዓቶች}other{# ሰዓቶች}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ደቂቃ}one{# ደቂቃዎች}other{# ደቂቃዎች}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 ሴኮ}one{# ሰከንዶች}other{# ሰከንዶች}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ቀን}one{# ቀን}other{# ቀናት}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ሰዓት}one{# ሰዓት}other{# ሰዓታት}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# ደቂቃ}one{# ደቂቃ}other{# ደቂቃዎች}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ሰከንድ}one{# ሰከንድ}other{# ሰከንዶች}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ማናቸውም ፈቃድ"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"በማንኛውም ጊዜ"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ባለፉት 7 ቀኖች"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ባለፉት 24 ሰዓቶች"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"ባለፈው 1 ሰዓት"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ባለፉት 15 ደቂቃዎች"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ባለፈው 1 ደቂቃ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ያለፈው # ቀን}one{ያለፈው # ቀን}other{ያለፉት # ቀናት}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{ባለፈው # ሰዓት}one{ባለፈው # ሰዓት}other{ባለፉት # ሰዓታት}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ባለፈው # ደቂቃ}one{ባለፈው # ደቂቃ}other{ባለፉት # ደቂቃዎች}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ምንም ፈቃድ አጠቃቀሞች የሉም"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"በማናቸውም ጊዜ ያለ የቅርብ ጊዜ መዳረስ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ባለፉት 7 ቀናት በጣም የቅርብ ጊዜ መዳረስ"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ባለፈው 1 ሰዓት የፈቃድ አጠቃቀም"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ባለፉት 15 ደቂቃዎች ውስጥ የፈቃድ አጠቃቀም"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ባለፈው 1 ደቂቃ የፈቃድ አጠቃቀም"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ባለፉት 24 ሰዓታት ውስጥ ስራ ላይ አልዋለም"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ባለፉት 7 ቀናት ውስጥ ስራ ላይ አልዋለም"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ባለፈው # ቀን ውስጥ ስራ ላይ አልዋለም}one{ባለፈው # ቀን ውስጥ ስራ ላይ አልዋለም}other{ባለፉት # ቀናት ውስጥ ስራ ላይ አልዋለም}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ባለፈው # ሰዓት ውስጥ ስራ ላይ አልዋለም}one{ባለፈው # ሰዓት ውስጥ ስራ ላይ አልዋለም}other{ባለፉት # ሰዓታት ውስጥ ስራ ላይ አልዋለም}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{በ1 መተግበሪያ ስራ ላይ ውሏል}one{በ# መተግበሪያዎች ስራ ላይ ውለዋል}other{በ# መተግበሪያዎች ስራ ላይ ውለዋል}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ሁሉንም በዳሽ ቦርድ ውስጥ ይመልከቱ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"የተጣራው በ፦ <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ለሚዲያ ብቻ መዳረሻ ይፍቀዱ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ሁልጊዜ ፍቀድ"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"መተግበሪያዎን እየተጠቀሙ እያሉ ብቻ ይፍቀዱ"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"ሁልጊዜ ጠይቅ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"አትፍቀድ"</string> <string name="precise_image_description" msgid="6349638632303619872">"ትክክለኛ አካባቢ"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"አይፈቀድም"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ሁሉንም ፋይሎች መድረስ የሚችሉ ተጨማሪ መተግበሪያዎችን ይመልከቱ"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ቀን}one{# ቀኖች}other{# ቀኖች}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ሰዓት}one{# ሰዓቶች}other{# ሰዓቶች}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 ደቂቃ}one{# ደቂቃዎች}other{# ደቂቃዎች}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 ሰከንድ}one{# ሰከንዶች}other{# ሰከንዶች}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ሰዓት}one{# ሰዓት}other{# ሰዓታት}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# ደቂቃ}one{# ደቂቃ}other{# ደቂቃዎች}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ሰከንድ}one{# ሰከንድ}other{# ሰከንዶች}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"የፈቃድ አስታዋሾች"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ጥቅም ላይ ያልዋለ መተግበሪያ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ጥቅም ላይ ያልዋሉ መተግበሪያዎች"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> በመሣሪያዎ ላይ ያሉ <b>ፎቶዎችን፣ ቪዲዮዎችን፣ ሙዚቃን፣ ኦዲዮን፣ ቪዲዮዎችን እና ሌሎች ፋይሎችን</b> ዘንድ እንዲደርስ ይፈቀድለት?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> በዚህ መሣሪያ ላይ ያለ ሙዚቃን እና ሌሎች የኦዲዮ ፋይሎችን እንዲደርስ ይፈቀድለት?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> በዚህ መሣሪያ ላይ ያሉ ፎቶዎችን እና ቪዲዮዎችን እንዲደርስ ይፈቀድለት?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ኦዲዮን እንዲቀዳ ይፈቀድለት?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"መተግበሪያው ኦዲዮን መቅዳት የሚችለው መተግበሪያውን እርስዎ ሲጠቀሙበት ብቻ ነው"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ኦዲዮን እንዲቀዳ ይፈቀድለት?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"መተግበሪያዎች ጽሑፍን፣ ምስሎችን ወይም እርስዎ የቀዱትን ሌላ ይዘት ሲደርሱ መልዕክት አሳይ"</string> <string name="show_password_title" msgid="2877269286984684659">"የይለፍ ቃላትን አሳይ"</string> <string name="show_password_summary" msgid="1110166488865981610">"እርስዎ በሚተይቡበት ጊዜ ቁምፊዎችን በአጭሩ ያሳይ"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml index 6a8f6aa9b..89918c32b 100644 --- a/PermissionController/res/values-ar/strings.xml +++ b/PermissionController/res/values-ar/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"عدم تغيير الإذن \"السماح فقط أثناء استخدام التطبيق\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"مواصلة استخدام الإذن \"هذه المرة فقط\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"معلومات أكثر"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"عدم السماح على أي حال"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"رفض"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> من <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{يوم واحد}zero{# يوم}two{يومان}few{# أيام}many{# يومًا}other{# يوم}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{ساعة واحدة}zero{# ساعة}two{ساعتان}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان}few{# دقائق}many{# دقيقةً}other{# دقيقة}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{ثانية واحدة}zero{# ثانية}two{ثانيتان}few{# ثوانٍ}many{# ثانيةً}other{# ثانية}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{يوم واحد}zero{# يوم}two{يومان}few{# أيام}many{# يومًا}other{# يوم}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{ساعة واحدة}zero{# ساعة}two{ساعتان}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان}few{# دقائق}many{# دقيقة}other{# دقيقة}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{ثانية واحدة}zero{# ثانية}two{ثانيتان}few{# ثوانٍ}many{# ثانيةً}other{# ثانية}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"أيّ إذن"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"أي وقت"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"آخر 7 أيام"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"آخر 24 ساعة"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"آخر ساعة"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"آخر 15 دقيقة"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"الدقيقة الماضية"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{آخر يوم}zero{آخر # يوم}two{آخر يومَين}few{آخر # أيام}many{آخر # يومًا}other{آخر # يوم}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{آخر ساعة}zero{آخر # ساعة}two{آخر ساعتين}few{آخر # ساعات}many{آخر # ساعة}other{آخر # ساعة}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{آخر دقيقة}zero{آخر # دقيقة}two{آخر دقيقتين}few{آخر # دقائق}many{آخر # دقيقة}other{آخر # دقيقة}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"لم يتمّ استخدام الأذونات"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"آخر إذن وصول في أي وقت"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"الاستخدامات الحديثة لإذن الوصول خلال آخر 7 أيام"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"استخدام الإذن خلال الساعة الأخيرة"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"استخدام الإذن خلال آخر 15 دقيقة"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"استخدام الإذن خلال الدقيقة الماضية"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"لم يتم استخدام الإذن في آخر 24 ساعة."</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"لم يتم استخدام الإذن في آخر 7 أيام."</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{لم يتم استخدام الإذن في اليوم السابق.}zero{لم يتم استخدام الإذن في الأيام الـ # السابقة.}two{لم يتم استخدام الإذن في اليومين السابقين.}few{لم يتم استخدام الإذن في الأيام الـ # السابقة.}many{لم يتم استخدام الإذن في الأيام الـ # السابقة.}other{لم يتم استخدام الإذن في الأيام الـ # السابقة.}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{لم يتم استخدام الإذن خلال الساعة السابقة}zero{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}two{لم يتم استخدام الإذن خلال الساعتين السابقتين.}few{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}many{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}other{لم يتم استخدام الإذن خلال الساعات الـ # السابقة.}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{يستخدِمه تطبيق واحد.}zero{يستخدِمه # تطبيق.}two{يستخدِمه تطبيقان.}few{يستخدِمه # تطبيقات.}many{يستخدِمه # تطبيقًا.}other{يستخدِمه # تطبيق.}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"الاطّلاع على الكل في \"لوحة البيانات\""</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"تمّت الفلترة حسب: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"السماح بالوصول إلى الوسائط فقط"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"السماح طوال الوقت"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"السماح عند استخدام التطبيق فقط"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"الطلب في كل مرة"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"عدم السماح"</string> <string name="precise_image_description" msgid="6349638632303619872">"الموقع الجغرافي الدقيق"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"الأذونات غير المسموح بها"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"الاطّلاع على تطبيقات أكثر يمكنها الوصول إلى كل الملفات"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{يوم واحد}zero{# يوم}two{يومان}few{# أيام}many{# يومًا}other{# يوم}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{ساعة واحدة}zero{# ساعة}two{ساعتان}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان}few{# دقائق}many{# دقيقةً}other{# دقيقة}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{ثانية واحدة}zero{# ثانية}two{ثانيتان}few{# ثوانٍ}many{# ثانيةً}other{# ثانية}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{ساعة واحدة}zero{# ساعة}two{ساعتان}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان}few{# دقائق}many{# دقيقةً}other{# دقيقة}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{ثانية واحدة}zero{# ثانية}two{ثانيتان}few{# ثوانٍ}many{# ثانية}other{# ثانية}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"تذكيرات الأذونات"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"تطبيق واحد غير مستخدم"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"عدد التطبيقات غير المُستخدمة: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"هل تسمح بوصول <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> إلى <b>الصور والفيديوهات والموسيقى والملفات الصوتية وملفات أخرى</b> على هذا الجهاز؟"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"هل تريد السماح بوصول <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> إلى المقاطع الموسيقية والملفات الصوتية على هذا الجهاز؟"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"هل تريد السماح بوصول <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> إلى الصور والفيديوهات على هذا الجهاز؟"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"لن يتمكن هذا التطبيق من تسجيل الصوت إلا عندما يكون قيد الاستخدام"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"عرض رسالة عندما يصل التطبيق إلى نص أو صور أو محتوى آخر تم نسخه."</string> <string name="show_password_title" msgid="2877269286984684659">"عرض كلمات المرور"</string> <string name="show_password_summary" msgid="1110166488865981610">"عرض الأحرف لفترة وجيزة أثناء الكتابة"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml index c339ec354..14b1b8d61 100644 --- a/PermissionController/res/values-as/strings.xml +++ b/PermissionController/res/values-as/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“এপ্টো ব্যৱহাৰ হৈ থকা অৱস্থাত” ৰাখক"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“কেৱল এইবাৰৰ বাবে অনুমতি দিয়ক” বিকল্পটো ৰাখক"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"অধিক তথ্য"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"আটাইবোৰ ফট’ এক্সেছৰ অনুমতি দিয়ক"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ফট’ বাছনি কৰক"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"অধিক ফট’ বাছক"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"অধিক ফট’ বাছনি নকৰিব"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"তথাপি অনুমতি নিদিব"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"অগ্ৰাহ্য কৰক"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>ৰ ভিতৰত <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>টা"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{১ মিনিট}one{# মিনিট}other{# মিনিট}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{১ ছেকেণ্ড}one{# ছেকেণ্ড}other{# ছেকেণ্ড}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# দিন}one{# দিন}other{# দিন}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ছেকেণ্ড}one{# ছেকেণ্ড}other{# ছেকেণ্ড}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"যিকোনো অনুমতি"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"যিকোনো সময়ত"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"যোৱা ৭ দিনত"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"যোৱা ২৪ ঘণ্টাত"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"যোৱা ১ ঘণ্টাত"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"যোৱা ১৫ মিনিটত"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"যোৱা ১ মিনিটত"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{যোৱা # দিনত}one{যোৱা # দিনত}other{যোৱা # দিনত}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{যোৱা # ঘণ্টাত}one{যোৱা # ঘণ্টাত}other{যোৱা # ঘণ্টাত}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{যোৱা # মিনিটত}one{যোৱা # মিনিটত}other{যোৱা # মিনিটত}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"অনুমতি ব্যৱহাৰ কৰা হোৱা নাই"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"সৰ্বসময়ৰ একেবাৰে শেহতীয়াকৈ কৰা এক্সেছ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"যোৱা ৭ দিনত একেবাৰে শেহতীয়া এক্সেছ"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"যোৱা ১ মিনিটত কৰা অনুমতিৰ ব্যৱহাৰ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"যোৱা ১৫ মিনিটত কৰা অনুমতিৰ ব্যৱহাৰ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"যোৱা ১ মিনিটত কৰা অনুমতিৰ ব্যৱহাৰ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"যোৱা ২৪ ঘণ্টাত ব্যৱহাৰ কৰা হোৱা নাই"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"যোৱা ৭ দিনত ব্যৱহাৰ কৰা হোৱা নাই"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{যোৱা # দিনত ব্যৱহাৰ কৰা হোৱা নাই}one{যোৱা # দিনত ব্যৱহাৰ কৰা হোৱা নাই}other{যোৱা # দিনত ব্যৱহাৰ কৰা হোৱা নাই}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{যোৱা # ঘণ্টাত ব্যৱহাৰ কৰা হোৱা নাই}one{যোৱা # ঘণ্টাত ব্যৱহাৰ কৰা হোৱা নাই}other{যোৱা # ঘণ্টাত ব্যৱহাৰ কৰা হোৱা নাই}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{১ টা এপে ব্যৱহাৰ কৰিছে}one{# টা এপে ব্যৱহাৰ কৰিছে}other{# টা এপে ব্যৱহাৰ কৰিছে}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ডেশ্বব’ৰ্ডত আটাইবোৰ চাওক"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"এই অনুসৰি ফিল্টাৰ কৰা: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"কেৱল মিডিয়ালৈ এক্সেছৰ অনুমতি দিয়ক"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"সকলো সময়ৰ বাবে অনুমতি দিয়ক"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"কেৱল এপ্ ব্যৱহাৰ হৈ থাকোঁতে অনুমতি দিয়ক"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"আটাইবোৰ ফট’ৰ অনুমতি দিয়ক"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"বাছনি কৰা ফট’ৰ অনুমতি দিয়ক"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"প্ৰতিবাৰতে সোধক"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"অনুমতি নিদিব"</string> <string name="precise_image_description" msgid="6349638632303619872">"সঠিক অৱস্থান"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"অনুমতি নাই"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"আটাইবোৰ ফাইল এক্সেছ কৰিব পৰা অধিক এপ্ চাওক"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{১ মিনিট}one{# মিনিট}other{# মিনিট}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{১ ছেকেণ্ড}one{# ছেকেণ্ড}other{# ছেকেণ্ড}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ছেকেণ্ড}one{# ছেকেণ্ড}other{# ছেকেণ্ড}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"অনুমতি বিষয়ক ৰিমাইণ্ডাৰ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"১ টা অব্যৱহৃত এপ্"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> টা অব্যৱহৃত এপ্"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক এই ডিভাইচটোত থকা <b>ফট’, ভিডিঅ’, সংগীত, অডিঅ’ আৰু অন্য ফাইল</b> এক্সেছ কৰিবলৈ দিবনে?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক এই ডিভাইচটোত থকা সংগীত আৰু অডিঅ’ এক্সেছ কৰিবলৈ অনুমতি দিবনে?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক এই ডিভাইচটোত থকা ফট’ আৰু ভিডিঅ’ এক্সেছ কৰিবলৈ অনুমতি দিবনে?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক অধিক ফট’ এক্সেছ কৰাৰ অনুমতি দিবনে?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক অডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"আপুনি এই এপ্টো ব্যৱহাৰ কৰি থকাৰ সময়তহে কেৱল ই অডিঅ’ ৰেকৰ্ড কৰিব পাৰিব"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক অডিঅ’ ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"এপে আপুনি প্ৰতিলিপি কৰা পাঠ, প্ৰতিচ্ছবি অথবা অন্য সমল এক্সেছ কৰিলে এটা বাৰ্তা দেখুৱাওক"</string> <string name="show_password_title" msgid="2877269286984684659">"পাছৱৰ্ডবোৰ দেখুৱাওক"</string> <string name="show_password_summary" msgid="1110166488865981610">"আপুনি টাইপ কৰাৰ লগে লগে বৰ্ণসমূহ খন্তেকৰ বাবে দেখুৱাওক"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"এই এপ্টোৱে তৃতীয় পক্ষৰ সৈতে <xliff:g id="PERMISSION_NAME">%s</xliff:g>ৰ ডেটা শ্বেয়াৰ কৰিব পাৰে বুলি জনাইছে"</string> </resources> diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml index ca119aa53..a63a5ead2 100644 --- a/PermissionController/res/values-az/strings.xml +++ b/PermissionController/res/values-az/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Tətbiq istifadə edilən zaman” saxlansın"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Ancaq bu dəfə” saxlanılsın"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ətraflı məlumat"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Bütün fotolara girişə icazə verin"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotolar seçin"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Daha çox foto seçin"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Daha çox foto seçməyin"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"İstənilən halda icazə verməyin"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"İmtina edin"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 gün}other{# gün}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 saat}other{# saat}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 dəq}other{# dəq}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 san}other{# san}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# gün}other{# gün}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# saat}other{# saat}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# dəq}other{# dəq}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# san}other{# san}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Hər hansı icazə"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"İstənilən vaxt"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Son 7 gün"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Son 24 saat"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Son 1 saat"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Son 15 dəqiqə"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Son 1 dəqiqə"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Son # gün}other{Son # gün}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Son # saat}other{Son # saat}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Son # dəqiqə}other{Son # dəqiqə}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"İcazələrdən istifadə olunmayıb"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"İstənilən vaxt üçün fəaliyyət"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Son 7 gün ərzindəki fəaliyyət"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Son 1 saat ərzində icazə istifadəsi"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Son 15 dəqiqə ərzində icazə istifadəsi"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Son 1 dəqiqə ərinzdə icazə istifadəsi"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Son 24 saat ərzində istifadə edilməyib"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Son 7 gün ərzində istifadə edilməyib"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Son # gün ərzində istifadə edilməyib}other{Son # gün ərzində istifadə edilməyib}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Son # saat ərzində istifadə edilməyib}other{Son # saat ərzində istifadə edilməyib}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 tətbiq istifadə edir}other{# tətbiq istifadə edir}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Hamısına İdarə panelində baxın"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrlədi: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Yalnız mediaya giriş icazəsi verin"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Həmişə icazə verin"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Yalnız istifadə zamanı"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Bütün fotolara icazə verin"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Seçilmiş fotolara icazə verin"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Həmişə soruşulsun"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"İcazə verməyin"</string> <string name="precise_image_description" msgid="6349638632303619872">"Dəqiq məkan"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"İcazə verilməyib"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bütün fayllara giriş edə bilən digər tətbiqlərə baxın"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 gün}other{# gün}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 saat}other{# saat}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 dəqiqə}other{# dəqiqə}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 saniyə}other{# saniyə}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# saat}other{# saat}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# dəqiqə}other{# dəqiqə}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# saniyə}other{# saniyə}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"İcazə xatırladıcıları"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 tətbiq istifadə edilmir"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> istifadə olunmayan tətbiq"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin bu cihazdakı <b>foto, video, musiqi, audio və digər fayllara</b> girişinə icazə verilsin?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə bu cihazdakı musiqi və audioya girişinə icazə verilsin?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin cihazdakı foto və videolara girişinə icazə verilsin?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin daha çox fotoya girişinə icazə verilsin?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə səs yazmaq icazəsi verilsin?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Tətbiq yalnız ondan istifadə etiyiniz zaman audio yaza biləcək"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə audio yazmaq icazəsi verilsin?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Tətbiq kopyalanmış mətn, şəkil və ya digər kontent işlədəndə bildiriş göstərilsin"</string> <string name="show_password_title" msgid="2877269286984684659">"Parolları göstərin"</string> <string name="show_password_summary" msgid="1110166488865981610">"Yazarkən simvollar qısa müddət göstərilsin"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu tətbiq <xliff:g id="PERMISSION_NAME">%s</xliff:g> datasını üçüncü tərəflərlə paylaşa biləcəyini bildirdi"</string> </resources> diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml index 32a2295f6..fcd778bba 100644 --- a/PermissionController/res/values-b+sr+Latn/strings.xml +++ b/PermissionController/res/values-b+sr+Latn/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži „Dok se aplikacija koristi“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži Samo ovaj put"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dozvoli pristup svim slikama"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Izaberite slike"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Izaberite još slika"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nemojte da birate više slika"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ionako ne dozvoli"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek}one{# sek}few{# sek}other{# sek}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dan}one{# dan}few{# dana}other{# dana}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek}one{# sek}few{# sek}other{# sek}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Bilo koja dozvola"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Bilo kada"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Poslednjih 7 dana"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Poslednja 24 sata"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Poslednji sat"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Poslednjih 15 minuta"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Poslednji minut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Poslednji # dan}one{Poslednji # dan}few{Poslednja # dana}other{Poslednjih # dana}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Poslednji # sat}one{Poslednji # sat}few{Poslednja # sata}other{Poslednjih # sati}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Poslednji # minut}one{Poslednji # minut}few{Poslednja # minuta}other{Poslednjih # minuta}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Dozvole nisu korišćene"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Najskoriji pristup u bilo kom trenutku"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Najskoriji pristup u poslednjih 7 dana"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Korišćenje dozvola u poslednjih sat vremena"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Korišćenje dozvole u poslednjih 15 minuta"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Korišćenje dozvola u poslednjem minutu"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nije korišćeno u poslednja 24 sata"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nije korišćeno u poslednjih 7 dana"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nije korišćeno tokom poslednjeg # dana}one{Nije korišćeno tokom poslednjeg # dana}few{Nije korišćeno tokom poslednja # dana}other{Nije korišćeno tokom poslednjih # dana}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nije korišćeno tokom poslednjeg # sata}one{Nije korišćeno tokom poslednjeg # sata}few{Nije korišćeno tokom poslednja # sata}other{Nije korišćeno tokom poslednjih # sati}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Koristi 1 aplikacija}one{Koristi # aplikacija}few{Koriste # aplikacije}other{Koristi # aplikacija}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Prikaži sve na kontrolnoj tabli"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrirano prema: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dozvoli samo pristup medijima"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dozvoli uvek"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dozv. samo dok se apl. koristi"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dozvoli sve slike"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dozvoli izabrane slike"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dozvoli"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precizna lokacija"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nije dozvoljeno"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Prikaži još aplikacija sa pristupom svim fajlovima"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minut}one{# minut}few{# minuta}other{# minuta}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunda}one{# sekunda}few{# sekunde}other{# sekundi}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}one{# minut}few{# minuta}other{# minuta}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunda}one{# sekunda}few{# sekunde}other{# sekundi}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Podsetnici za dozvole"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplikacija koja se ne koristi"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Aplikacija koje se ne koriste: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Pristup slikama, videu, muzici, zvuku i drugom na uređaju za <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoljavate li pristup muzici i zvuku na ovom uređaju za <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoljavate li pristup slikama i videu na ovom uređaju za <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Želite li da aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> dozvolite pristup većem broju slika?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima zvuk?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći da snima zvuk samo dok koristite aplikaciju"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima zvuk?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Prikazuje poruku kada aplikacije pristupaju tekstu, slikama ili drugom sadržaju koji ste kopirali"</string> <string name="show_password_title" msgid="2877269286984684659">"Prikazuj lozinke"</string> <string name="show_password_summary" msgid="1110166488865981610">"Prikazuje znakove nakratko dok kucate"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ova aplikacija navodi da može da deli podatke (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) sa trećim stranama"</string> </resources> diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml index a45533fd6..94fc7b22b 100644 --- a/PermissionController/res/values-be/strings.xml +++ b/PermissionController/res/values-be/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Пакінуць \"У актыўным рэжыме праграмы\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Захаваць толькі на гэты раз"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Падрабязней"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дазволіць доступ да ўсіх фота"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Выбраць фота"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Выбраць больш фота"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Больш не выбіраць фота"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Усё роўна не дазваляць"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Адхіліць"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 дзень}one{# дзень}few{# дні}many{# дзён}other{# дня}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 хв}one{# хв}few{# хв}many{# хв}other{# хв}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 с}one{# с}few{# с}many{# с}other{# с}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# дзень}one{# дзень}few{# дні}many{# дзён}other{# дня}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# хв}one{# хв}few{# хв}many{# хв}other{# хв}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# с}one{# с}few{# с}many{# с}other{# с}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Любы дазвол"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"За любы час"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"За апошнія 7 дзён"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"За апошнія 24 гадзіны"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"За апошнюю гадзіну"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"За апошнія 15 хвілін"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"За апошнюю хвіліну"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{За апошні # дзень}one{За апошні # дзень}few{За апошнія # дні}many{За апошнія # дзён}other{За апошнія # дня}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{За апошнюю # гадзіну}one{За апошнюю # гадзіну}few{За апошнія # гадзіны}many{За апошнія # гадзін}other{За апошнія # гадзіны}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{За апошнюю # хвіліну}one{За апошнюю # хвіліну}few{За апошнія # хвіліны}many{За апошнія # хвілін}other{За апошнія # хвіліны}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Без выкарыстання дазволаў"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Доступы за ўвесь час"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Доступы за апошнія 7 дзён"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Выкарыстанне дазволаў за апошнюю гадзіну"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Выкарыстаннне дазволаў за апошнія 15 хвілін"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Выкарыстанне дазволаў за апошнюю хвіліну"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"За апошнія 24 гадзіны доступ не выкарыстоўваўся"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"За апошнія 7 сутак доступ не выкарыстоўваўся"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{За апошнія # суткі дазвол не выкарыстоўваўся}one{За апошнія # суткі дазвол не выкарыстоўваўся}few{За апошнія # сутак дазвол не выкарыстоўваўся}many{За апошнія # сутак дазвол не выкарыстоўваўся}other{За апошнія # сутак дазвол не выкарыстоўваўся}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{За апошнюю # гадзіну дазвол не выкарыстоўваўся}one{За апошнюю # гадзіну дазвол не выкарыстоўваўся}few{За апошнія # гадзіны дазвол не выкарыстоўваўся}many{За апошнія # гадзін дазвол не выкарыстоўваўся}other{За апошнія # гадзіны дазвол не выкарыстоўваўся}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Выкарыстоўваецца 1 праграмай}one{Выкарыстоўваецца # праграмай}few{Выкарыстоўваецца # праграмамі}many{Выкарыстоўваецца # праграмамі}other{Выкарыстоўваецца # праграмы}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Паказаць усе на панэлі кіравання"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Параметр фільтравання: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дазволіць доступ толькі да мультымедыя"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дазволіць заўсёды"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дазволіць толькі падчас карыстання праграмай"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дазволіць усе фота"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дазволіць выбраныя фота"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Заўсёды пытацца"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дазваляць"</string> <string name="precise_image_description" msgid="6349638632303619872">"Дакладнае месцазнаходжанне"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Забаронена"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Праглядзець іншыя праграмы, якія маюць доступ да ўсіх файлаў"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 дзень}one{# дзень}few{# дні}many{# дзён}other{# дня}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 хвіліна}one{# хвіліна}few{# хвіліны}many{# хвілін}other{# хвіліны}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# хвіліна}one{# хвіліна}few{# хвіліны}many{# хвілін}other{# хвіліны}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Напаміны пра дазволы"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 праграма не выкарыстоўваецца"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Колькасць праграм не ў карыстанні: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да <b>фота, відэа, музыкі, аўдыя і файлаў</b> на гэтай прыладзе?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да музыкі і аўдыя на гэтай прыладзе?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да фота і відэа на гэтай прыладзе?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Адкрыць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да большай колькасці фота?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> запісваць аўдыя?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Гэта праграма зможа запісваць аўдыя толькі падчас яе выкарыстання"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> запісваць аўдыя?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Паказваць паведамленне, калі праграмы атрымліваюць доступ да тэксту, відарысаў ці іншага змесціва, якое вы скапіравалі"</string> <string name="show_password_title" msgid="2877269286984684659">"Паказваць паролі"</string> <string name="show_password_summary" msgid="1110166488865981610">"Падчас уводу сімвалаў на кароткі час паказваць іх"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Гэта праграма можа абагульваць з трэцімі бакамі даныя катэгорыі \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\""</string> </resources> diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml index 0275e3353..7bf609975 100644 --- a/PermissionController/res/values-bg/strings.xml +++ b/PermissionController/res/values-bg/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Запазване на опцията „Докато приложението се използва“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Запазване на настройката „Само този път“"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Още информация"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Разрешаване на достъп до всички снимки"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Избиране на снимки"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Избиране на още снимки"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Без избиране на още снимки"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Забраняване въпреки това"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Отхвърляне"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> от <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ден}other{# дни}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 час}other{# часа}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мин}other{# мин}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 сек}other{# сек}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ден}other{# дни}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# час}other{# часа}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мин}other{# мин}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# сек}other{# сек}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Всички разрешения"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"По всяко време"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Последните 7 дни"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Последните 24 часа"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Последния 1 час"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Последните 15 минути"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Последната минута"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Последният # ден}other{Последните # дни}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Последният # час}other{Последните # часа}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Последната # минута}other{Последните # минути}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Разрешенията не са използвани"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Най-скорошен достъп във всеки един момент"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Най-скорошен достъп през последните 7 дни"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Използвани разрешения през последния час"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Използвани разрешения през последните 15 минути"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Използвани разрешения през последната минута"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Не е използвано през последните 24 часа"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Не е използвано през последните 7 дни"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Не е използвано през последния # ден}other{Не е използвано през последните # дни}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Не е използвано през последния # час}other{Не е използвано през последните # часа}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Използва се от 1 приложение}other{Използва се от # приложения}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Преглед на всичко в таблото за управление"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Филтрирано по: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Разрешаване на достъп само до мултимедията"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Разрешаване във всички случаи"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Разрешаване само докато приложението се използва"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Разрешаване на всички снимки"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Разрешаване на избраните снимки"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Извеждане на запитване всеки път"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Забраняване"</string> <string name="precise_image_description" msgid="6349638632303619872">"Точно местоположение"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Не е разрешено"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Вижте още приложения, които имат достъп до всички файлове"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ден}other{# дни}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 час}other{# часа}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минута}other{# минути}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}other{# секунди}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}other{# часа}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минута}other{# минути}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}other{# секунди}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Напомняния за разрешения"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 неизползвано приложение"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> неизползвани приложения"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Разр. на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> достъп до <b>снимките, видео- и аудиосъдърж. и другите файлове</b> на у-вото?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до музиката и аудиофайловете на това устройство?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до снимките и видеоклиповете на това устройство?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Да се предостави ли достъп на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> до още снимки?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да записва аудио?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Приложението ще може да записва аудио само когато го използвате"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да записва аудио?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Показване на съобщение, когато приложенията осъществяват достъп до копирани от вас текст, изображения или друго съдържание"</string> <string name="show_password_title" msgid="2877269286984684659">"Показване на паролите"</string> <string name="show_password_summary" msgid="1110166488865981610">"Кратко показване на знаците, докато пишете"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Това приложение посочи, че може да споделя с трети страни данни за <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string> </resources> diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml index 356e84d92..658575eed 100644 --- a/PermissionController/res/values-bn/strings.xml +++ b/PermissionController/res/values-bn/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“অ্যাপ ব্যবহার করার সময়” বিকল্পটি চালু রাখুন"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“শুধুমাত্র এই সময়ে অনুমতি দিন” বিকল্পটি রাখুন"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"আরও তথ্য"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"সব ফটোতে অ্যাক্সেস দেওয়া"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ফটো বেছে নেওয়া"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"আরও ফটো বেছে নেওয়া"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"আরও ফটো বেছে নেবেন না"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"যাই হোক, অনুমতি দেবেন না"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"বাতিল করুন"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>টির মধ্যে <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> নম্বর"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{১ মিনিট}one{# মিনিট}other{# মিনিট}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{১ সেকেন্ড}one{# সেকেন্ড}other{# সেকেন্ড}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# দিন}one{# দিন}other{# দিন}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# সেকেন্ড}one{# সেকেন্ড}other{# সেকেন্ড}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"যেকোন অনুমতি"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"যেকোনও সময়"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"গত ৭ দিন"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"গত ২৪ ঘন্টায়"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"শেষ ১ ঘণ্টা"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"গত ১৫ মিনিট"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"গত ১ মিনিটে"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{গত # দিন}one{গত # দিন}other{গত # দিন}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{গত # ঘণ্টায়}one{গত # ঘণ্টায়}other{গত # ঘণ্টায়}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{গত # মিনিটে}one{গত # মিনিটে}other{গত # মিনিটে}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"কোন অনুমতির ব্যবহার হয়নি"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"সাম্প্রতিক ব্যবহার"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"গত ৭ দিনের মধ্যে সাম্প্রতিক অ্যাক্সেস"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"গত ১ ঘণ্টার মধ্যে অনুমতির ব্যবহার"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"গত ১৫ মিনিটের মধ্যে অনুমতির ব্যবহার"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"গত ১ মিনিটের মধ্যে অনুমতির ব্যবহার"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"গত ২৪ ঘন্টায় ব্যবহার করা হয়নি"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"গত ৭ দিনে ব্যবহার করা হয়নি"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{গত # দিনে ব্যবহার করা হয়নি}one{গত # দিনে ব্যবহার করা হয়নি}other{গত # দিনে ব্যবহার করা হয়নি}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{গত # ঘণ্টায় ব্যবহার করা হয়নি}one{গত # ঘণ্টায় ব্যবহার করা হয়নি}other{গত # ঘণ্টায় ব্যবহার করা হয়নি}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{১টি অ্যাপ ব্যবহার করেছে}one{#টি অ্যাপ ব্যবহার করেছে}other{#টি অ্যাপ ব্যবহার করেছে}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"সবকিছু ড্যাশবোর্ডে দেখুন"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"এই অনুযায়ী ফিল্টার করা হয়েছে: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"শুধু মিডিয়া অ্যাক্সেসের অনুমতি দিন"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"সব সময়ের জন্য অনুমতি দিন"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"শুধু অ্যাপ ব্যবহারের সময় অনুমতি দিন"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"সব ফটোকে অনুমতি দেওয়া"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"বাছাই করা ফটোকে অনুমতি দেওয়া"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"প্রতিবার জিজ্ঞাসা করা হবে"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"অনুমতি দেবেন না"</string> <string name="precise_image_description" msgid="6349638632303619872">"সুনির্দিষ্ট লোকেশন"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"অনুমোদিত নয়"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"সব ফাইল অ্যাক্সেস করতে পারবে এমন আরও অ্যাপ দেখুন"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{১ মিনিট}one{# মিনিট}other{# মিনিট}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{১ সেকেন্ড}one{# সেকেন্ড}other{# সেকেন্ড}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# সেকেন্ড}one{# সেকেন্ড}other{# সেকেন্ড}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"অনুমতির রিমাইন্ডার"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"ব্যবহার হয়নি এমন ১টি অ্যাপ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"ব্যবহার হয়নি এমন <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>টি অ্যাপ"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"এই ডিভাইসে <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে <b>ফটো, ভিডিও, মিউজিক, অডিও ও অন্যান্য ফাইল</b> অ্যাক্সেসের অনুমতি দেবেন?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"এই ডিভাইসে <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে মিউজিক ও অডিও ফাইল অ্যাক্সেসের অনুমতি দেবেন?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"এই ডিভাইসে <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে ফটো ও ভিডিও অ্যাক্সেসের অনুমতি দেবেন?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"আরও ফটো অ্যাক্সেস করতে <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে অনুমতি দেবেন?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে অডিও রেকর্ড করার অনুমতি দেবেন?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"আপনি এই অ্যাপ ব্যবহার করার সময়েই শুধুমাত্র সেটি অডিও রেকর্ড করতে পারবে"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে অডিও রেকর্ড করার অনুমতি দিতে চান?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"আপনার কপি করা টেক্সট, ছবি বা অন্যান্য কন্টেন্ট অ্যাপ অ্যাক্সেস করলে মেসেজ দেখায়"</string> <string name="show_password_title" msgid="2877269286984684659">"পাসওয়ার্ড দেখুন"</string> <string name="show_password_summary" msgid="1110166488865981610">"টাইপ করার সময় অক্ষরগুলি কয়েক মুহূর্তের জন্য দেখুন"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"এই অ্যাপ, থার্ড-পার্টির সাথে <xliff:g id="PERMISSION_NAME">%s</xliff:g> সংক্রান্ত ডেটা শেয়ার করার অনুমতি চাইতে পারে"</string> </resources> diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml index 97a5e4395..589495cff 100644 --- a/PermissionController/res/values-bs/strings.xml +++ b/PermissionController/res/values-bs/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži “Kada se aplikacija koristi”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži “Samo ovaj put”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dozvoli pristup svim fotografijama"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Odaberi fotografije"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Odaberi više fotografija"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nemoj odabrati više fotografija"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Svejedno nemoj dozvoliti"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 h}one{# h}few{# h}other{# h}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}one{# s}few{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dan}one{# dan}few{# dana}other{# dana}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# h}one{# h}few{# h}other{# h}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}few{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Sva odobrenja"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Bilo kad"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Posljednjih 7 dana"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Posljednja 24 sata"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Posljednji sat"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Posljednjih 15 minuta"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Posljednja minuta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Posljednji dan}one{Posljednji # dan}few{Posljednja # dana}other{Posljednjih # dana}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Posljednji sat}one{Posljednjih # h}few{Posljednja # h}other{Posljednjih # h}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Posljednja minuta}one{Posljednja # min}few{Posljednje # min}other{Posljednjih # min}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Odobrenje nije upotrijebljeno"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Najnoviji pristup u bilo koje vrijeme"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Najnoviji pristup u posljednjih 7 dana"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Korištena odobrenja u posljenji 1 sat"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Korištena odobrenja u posljednjih 15 minuta"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Korištena odobrenja u protekloj minuti"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nije korišteno u posljednja 24 h"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nije korišteno u posljednjih 7 dana"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nije korišteno u proteklom danu}one{Nije korišteno u proteklom # danu}few{Nije korišteno u protekla # dana}other{Nije korišteno u proteklih # dana}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nije korišteno u proteklom satu}one{Nije korišteno u protekli # h}few{Nije korišteno u protekla # h}other{Nije korišteno u proteklih # h}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Koristi 1 aplikacija}one{Koristi # aplikacija}few{Koriste # aplikacije}other{Koristi # aplikacija}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Prikaži sve na kontrolnoj tabli"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrirano prema: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dozvoli pristup samo medijima"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dozvoli sve vrijeme"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dozvoli samo dok se aplikacija koristi"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dozvoli sve fotografije"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dozvoli odabrane fotografije"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dozvoliti"</string> <string name="precise_image_description" msgid="6349638632303619872">"Tačna lokacija"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nije dozvoljeno"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Pogledajte više aplikacija koje imaju pristup svim fajlovima"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 h}one{# h}few{# h}other{# h}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 s}one{# s}few{# s}other{# s}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# h}one{# h}few{# h}other{# h}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# s}one{# s}few{# s}other{# s}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Podsjetnici odobrenja"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nekorištena aplikacija"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Broj nekorištenih aplikacija: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dozvoliti da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa <b>foto/video/muzičkim/audio i drugim fajlovima</b> na uređaju?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoliti da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa muzici i zvuku na ovom uređaju?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoliti da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa fotografijama i videozapisima na ovom uređaju?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup dodatnim fotografijama?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da snima zvuk?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati zvuk samo za vrijeme korištenja"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snimanje zvuka?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vidite poruku kada aplikacije pristupe tekstu, slikama ili drugom sadržaju koji ste kopirali"</string> <string name="show_password_title" msgid="2877269286984684659">"Prikaži lozinke"</string> <string name="show_password_summary" msgid="1110166488865981610">"Kratko prikazivanje znakova dok pišete"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikacija je navela da može dijeliti podatke iz kategorije <xliff:g id="PERMISSION_NAME">%s</xliff:g> s trećim stranama"</string> </resources> diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml index 0f834c6ec..debd32c74 100644 --- a/PermissionController/res/values-ca/strings.xml +++ b/PermissionController/res/values-ca/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Només mentre s\'utilitza l\'aplicació"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantén el permís Només aquesta vegada"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Més informació"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permet l\'accés a totes les fotos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selecciona fotos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selecciona més fotos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionis més fotos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permetis de cap manera"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignora"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dia}other{# dies}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}other{# hores}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dia}other{# dies}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}other{# hores}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualsevol permís"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"En qualsevol moment"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 darrers dies"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Darreres 24 hores"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últims 15 minuts"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Últim minut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Darrer dia}other{# darrers dies}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Darrera hora}other{Darreres # hores}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Darrer minut}other{Darrers # minuts}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Cap ús de permisos"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Accés més recents en qualsevol moment"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Accés més recents durant els 7 darrers dies"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Ús de permisos durant l\'última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Ús de permisos durant els últims 15 minuts"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Ús de permisos durant l\'últim minut"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"No s\'ha utilitzat en les 24 darreres hores"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"No s\'ha utilitzat en els 7 darrers dies"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{No s\'ha utilitzat en el darrer dia}other{No s\'ha utilitzat en els darrers # dies}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{No s\'ha utilitzat en la darrera hora}other{No s\'ha utilitzat en les darreres # hores}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilitzat per 1 aplicació}other{Utilitzat per # aplicacions}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Mostra-ho tot al tauler"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrats per: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permet l\'accés només als fitxers multimèdia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permet sempre"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permet només mentre s\'utilitza l\'aplicació"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permet totes les fotos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permet les fotos seleccionades"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pregunta sempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"No permetis"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ubicació exacta"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Sense permís"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Mostra més aplicacions que poden accedir a tots els fitxers"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}other{# dies}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}other{# hores}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minut}other{# minuts}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segon}other{# segons}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}other{# hores}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}other{# minuts}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segon}other{# segons}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Recordatoris de permisos"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplicació no utilitzada"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplicacions no utilitzades"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als <b>vídeos, fotos, música, àudio i altres fitxers</b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la música i l\'àudio d\'aquest dispositiu?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a les fotos i els vídeos d\'aquest dispositiu?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a més fotos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> gravi àudio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'aplicació només podrà gravar àudio mentre l\'estiguis utilitzant"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> gravi àudio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostra un missatge quan les aplicacions accedeixen al text, a les imatges o a qualsevol altre contingut que hagis copiat"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostra les contrasenyes"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostra els caràcters breument mentre escrius"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aquesta aplicació ha indicat que és possible que comparteixi dades (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) amb tercers"</string> </resources> diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml index 17130da1c..b0cd7e2ee 100644 --- a/PermissionController/res/values-cs/strings.xml +++ b/PermissionController/res/values-cs/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Ponechat „Během používání aplikace“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Ponechat možnost Pouze tentokrát"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Další informace"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Povolit přístup ke všem fotkám"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vybrat fotky"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vybrat více fotek"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nevybírat více fotek"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Přesto nepovolit"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Zavřít"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 den}few{# dny}many{# dne}other{# dní}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodin}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}few{# min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}few{# s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# den}few{# dny}many{# dne}other{# dní}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hodinu}few{# hodiny}many{# hodiny}other{# hodin}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}few{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}few{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Všechna oprávnění"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Kdykoli"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Posledních 7 dní"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Posledních 24 hodin"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Poslední hodina"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Posledních 15 minut"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Poslední minuta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Poslední den}few{Poslední # dny}many{Posledních # dne}other{Posledních # dní}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Poslední hodina}few{Poslední # hodiny}many{Posledních # hodiny}other{Posledních # hodin}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Poslední minuta}few{Poslední # minuty}many{Posledních # minuty}other{Posledních # minut}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Žádné využití oprávnění"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Poslední využití kdykoli"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Poslední použití za 7 dnů"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Využití oprávnění za poslední hodinu"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Využití oprávnění za posledních 15 minut"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Využití oprávnění za poslední minutu"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Během uplynulých 24 hodin nepoužito"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Během posledních 7 dní nepoužito"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Během uplynulého dne nepoužito}few{Během uplynulých # dní nepoužito}many{Během uplynulých # dne nepoužito}other{Během uplynulých # dní nepoužito}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Během uplynulé hodiny nepoužito}few{Během uplynulých # hodin nepoužito}many{Během uplynulých # hodiny nepoužito}other{Během uplynulých # hodin nepoužito}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Využíváno 1 aplikací}few{Využíváno # aplikacemi}many{Využíváno # aplikace}other{Využíváno # aplikacemi}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Zobrazit vše na panelu"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrováno podle: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Povolit pouze přístup k médiím"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Povolit vždy"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Povolit jen při používání aplikace"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Povolit všechny fotky"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Povolit vybrané fotky"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pokaždé se zeptat"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovolovat"</string> <string name="precise_image_description" msgid="6349638632303619872">"Přesná poloha"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nepovoleno"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobrazit další aplikace s přístupem ke všem souborům"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 den}few{# dny}many{# dne}other{# dní}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodin}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuta}few{# minuty}many{# minuty}other{# minut}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunda}few{# sekundy}many{# sekundy}other{# sekund}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hodinu}few{# hodiny}many{# hodiny}other{# hodin}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuta}few{# minuty}many{# minuty}other{# minut}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunda}few{# sekundy}many{# sekundy}other{# sekund}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Připomenutí o oprávněních"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Nepoužívané aplikace: 1"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Nepoužívané aplikace: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k <b>fotkám, videím, hudbě, zvuku a dalším souborům</b> v zařízení?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k hudbě a zvuku v zařízení?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k fotkám a videím v zařízení?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Udělit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k více fotkám?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> nahrávat zvuk?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikace bude moci zaznamenávat zvuk, pouze když ji budete používat"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> nahrávat zvuk?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovat zprávu, když aplikace použijí text, obrázky nebo jiný obsah, který jste zkopírovali"</string> <string name="show_password_title" msgid="2877269286984684659">"Zobrazovat hesla"</string> <string name="show_password_summary" msgid="1110166488865981610">"Při psaní se budou krátce zobrazovat zadané znaky"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Tato aplikace uvedla, že může sdílet data <xliff:g id="PERMISSION_NAME">%s</xliff:g> se třetími stranami"</string> </resources> diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml index 181348a88..d0a1f8ddb 100644 --- a/PermissionController/res/values-da/strings.xml +++ b/PermissionController/res/values-da/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behold \"Mens appen er i brug\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behold \"Kun denne ene gang\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mere info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Giv adgang til alle billeder"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vælg billeder"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vælg flere billeder"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Vælg ikke flere billeder"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tillad ikke alligevel"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Luk"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ud af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dag}one{# dag}other{# dage}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 time}one{# time}other{# timer}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min.}one{# min.}other{# min.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek.}one{# sek.}other{# sek.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dag}one{# dag}other{# dage}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# time}one{# time}other{# timer}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min.}one{# min.}other{# min.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek.}one{# sek.}other{# sek.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Alle tilladelser"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Nogensinde"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"De seneste 7 dage"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"De seneste 24 timer"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Den seneste time"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"De seneste 15 minutter"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Seneste minut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Seneste dag}one{Seneste # dag}other{Seneste # dage}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Seneste time}one{Seneste # time}other{Seneste # timer}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Seneste minut}one{Seneste # minut}other{Seneste # minutter}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ingen brug af tilladelsen"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Seneste adgang nogensinde"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Seneste adgang i de sidste 7 dage"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Anvendte tilladelser den seneste time"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Anvendte tilladelser i de sidste 15 minutter"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Anvendte tilladelser det seneste minut"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ikke brugt i de seneste 24 timer"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Ikke brugt i de seneste 7 dage"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Ikke brugt i # dag}one{Ikke brugt i # dag}other{Ikke brugt i de seneste # dage}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Ikke brugt i # time}one{Ikke brugt i # time}other{Ikke brugt i de seneste # timer}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Anvendes af 1 app}one{Anvendes af # app}other{Anvendes af # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Se alt i kontrolpanelet"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtreret efter: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Tillad kun adgang til mediefiler"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillad altid"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Tillad kun, mens appen er i brug"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillad alle billeder"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillad valgte billeder"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Spørg hver gang"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Tillad ikke"</string> <string name="precise_image_description" msgid="6349638632303619872">"Præcis lokation"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ikke tilladt"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se flere apps, som kan tilgå alle filer"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}one{# dag}other{# dage}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 time}one{# time}other{# timer}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minut}one{# minut}other{# minutter}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekund}one{# sekund}other{# sekunder}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# time}one{# time}other{# timer}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}one{# minut}other{# minutter}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekund}one{# sekund}other{# sekunder}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Påmindelser om tilladelse"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app, du ikke bruger"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps, du ikke bruger"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til <b>billeder, videoer, musik, lyd og andre filer</b> på denne enhed?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til musik og lyd på denne enhed?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til billeder og videoer på denne enhed?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til flere billeder?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilladelse til at optage lyd?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan kun optage lyd, mens du bruger appen"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilladelse til at optage lyd?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vis en meddelelse, når apps får adgang til tekst, billeder eller andet indhold, du har kopieret"</string> <string name="show_password_title" msgid="2877269286984684659">"Vis adgangskoder"</string> <string name="show_password_summary" msgid="1110166488865981610">"Vis kort tegnene, mens du skriver"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Denne app har angivet, at den muligvis deler <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredjeparter"</string> </resources> diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml index 498ef4471..33b80a0a2 100644 --- a/PermissionController/res/values-de/strings.xml +++ b/PermissionController/res/values-de/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"„Wenn die App verwendet wird“ beibehalten"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Nur dieses Mal\" aktiviert lassen"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Weitere Infos"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Zugriff auf alle Fotos erlauben"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotos auswählen"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Weitere Fotos auswählen"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Keine weiteren Fotos auswählen"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Trotzdem nicht zulassen"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Schließen"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> von <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 Tag}other{# Tage}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 Stunde}other{# Stunden}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# Tag}other{# Tage}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# Stunde}other{# Stunden}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Alle Berechtigungen"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Beliebiger Zeitraum"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Letzte 7 Tage"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Letzte 24 Stunden"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Letzte Stunde"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Letzte 15 Minuten"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Letzte Minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Letzter Tag}other{Letzte # Tage}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Letzte Stunde}other{Letzte # Stunden}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Letzte Minute}other{Letzte # Minuten}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Keine Berechtigungen verwendet"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Letzter Zugriff zu einem beliebigen Zeitpunkt"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Letzter Zugriff in den letzten 7 Tagen"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Berechtigungsnutzungen (letzte Stunde)"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Berechtigungsnutzungen (letzte 15 Minuten)"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Berechtigungsnutzungen (letzte Minute)"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"In den letzten 24 Stunden nicht verwendet"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"In den letzten 7 Tagen nicht verwendet"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Innerhalb des letzten Tages nicht verwendet}other{In den letzten # Tagen nicht verwendet}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Innerhalb der letzten Stunde nicht verwendet}other{In den letzten # Stunden nicht verwendet}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Von 1 App verwendet}other{Von # Apps verwendet}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Alle im Dashboard ansehen"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Gefiltert nach: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Zugriff nur auf Mediendateien zulassen"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Immer zulassen"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Zugriff nur während der Nutzung der App zulassen"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Alle Fotos erlauben"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Ausgewählte Fotos erlauben"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Jedes Mal fragen"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nicht zulassen"</string> <string name="precise_image_description" msgid="6349638632303619872">"Auf genauen Standort zugreifen"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nicht zugelassen"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Weitere Apps anzeigen, die auf alle Dateien zugreifen können"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 Tag}other{# Tage}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 Stunde}other{# Stunden}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 Minute}other{# Minuten}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 Sekunde}other{# Sekunden}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# Stunde}other{# Stunden}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# Minute}other{# Minuten}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# Sekunde}other{# Sekunden}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Berechtigungserinnerungen"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nicht verwendete App"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> nicht verwendete Apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, auf <b>Foto-, Video-, Musik-, Audio- und andere Dateien</b> auf diesem Gerät zuzugreifen?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, auf Musik- und Audiodateien auf diesem Gerät zuzugreifen?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, auf Fotos und Videos auf diesem Gerät zuzugreifen?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> Zugriff auf weitere Fotos erlauben?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, Audioaufnahmen zu machen?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Diese App kann nur Audioaufnahmen machen, solange du sie verwendest"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, Audioaufnahmen zu machen?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Eine Meldung wird angezeigt, wenn Apps auf Text, Bilder oder andere Inhalte zugreifen, die du kopiert hast"</string> <string name="show_password_title" msgid="2877269286984684659">"Passwörter anzeigen"</string> <string name="show_password_summary" msgid="1110166488865981610">"Zeichen während der Eingabe kurz anzeigen"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Für diese App wurde angegeben, dass sie eventuell Daten vom Typ „<xliff:g id="PERMISSION_NAME">%s</xliff:g>“ an Dritte weitergibt"</string> </resources> diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml index 045d2a1d6..fba0f6159 100644 --- a/PermissionController/res/values-el/strings.xml +++ b/PermissionController/res/values-el/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Διατήρηση της επιλογής \"Όταν χρησιμοποιείται η εφαρμογή\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Διατήρηση μόνο αυτήν τη φορά"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Περισσότερα"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Να επιτρέπεται η πρόσβαση σε όλες τις φωτογραφίες"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Επιλογή φωτογραφιών"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Επιλογή περισσότερων φωτογραφιών"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Να μην επιλεγούν περισσότερες φωτογραφίες"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Να μην επιτρέπεται καθόλου"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Παράβλεψη"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> από <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ημέρα}other{# ημέρες}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ώρα}other{# ώρες}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 λεπτό}other{# λεπτά}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 δευτ.}other{# δευτ.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ημέρα}other{# ημέρες}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ώρα}other{# ώρες}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# λεπτό}other{# λεπτά}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# δευτερόλεπτο}other{# δευτερόλεπτα}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Οποιαδήποτε άδεια"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"οποιαδήποτε στιγμή"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"τελευταίες 7 ημέρες"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"τελευταίες 24 ώρες"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"τελευταία 1 ώρα"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"τελευταία 15 λεπτά"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"τελευταίο 1 λεπτό"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Τελευταία # ημέρα}other{Τελευταίες # ημέρες}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Τελευταία # ώρα}other{Τελευταίες # ώρες}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Τελευταίο # λεπτό}other{Τελευταία # λεπτά}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Καμία χρήση δικαιωμάτων"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Πιο πρόσφατη πρόσβαση ανά πάσα στιγμή"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Πιο πρόσφατη πρόσβαση τις τελευταίες 7 ημέρες"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Χρήση άδειας την τελευταία 1 ώρα"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Χρήση αδειών τα τελευταία 15 λεπτά"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Χρήση άδειας το τελευταίο 1 λεπτό"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Δεν έχει χρησιμοποιηθεί τις τελευταίες 24 ώρες"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Δεν έχει χρησιμοποιηθεί τις τελευταίες επτά ημέρες"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Δεν έχει χρησιμοποιηθεί την τελευταία # ημέρα}other{Δεν έχει χρησιμοποιηθεί τις τελευταίες # ημέρες}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Δεν έχει χρησιμοποιηθεί την τελευταία # ώρα}other{Δεν έχει χρησιμοποιηθεί τις τελευταίες # ώρες}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Χρησιμοποιείται από 1 εφαρμογή}other{Χρησιμοποιείται από # εφαρμογές}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Εμφάνιση όλων στον Πίνακα ελέγχου"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Φιλτράρισμα κατά: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Να επιτρέπεται η διαχείριση μόνο των μέσων"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Να επιτρέπεται πάντα"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Μόνο με τη χρήση της εφαρμογής"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Να επιτρέπονται όλες οι φωτογραφίες"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Να επιτρέπονται οι επιλεγμένες φωτογραφίες"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Να ερωτώμαι κάθε φορά"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Να μην επιτρέπεται"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ακριβής τοποθεσία"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Δεν επιτρέπεται"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Δείτε περισσότερες εφαρμογές με πρόσβαση σε όλα τα αρχεία"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ημέρα}other{# ημέρες}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ώρα}other{# ώρες}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 λεπτό}other{# λεπτά}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 δευτερόλεπτο}other{# δευτερόλεπτα}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ώρα}other{# ώρες}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# λεπτό}other{# λεπτά}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# δευτερόλεπτο}other{# δευτερόλεπτα}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Υπενθυμίσεις άδειας"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 εφαρμογή που δεν χρησιμοποιείται"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> εφαρμογές που δεν χρησιμοποιούνται"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε <b>φωτογραφίες, βίντεο, μουσική, ήχο και άλλα αρχεία</b> της συσκευής;"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στη μουσική και στα αρχεία ήχου αυτής της συσκευής;"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στις φωτογραφίες και τα βίντεο αυτής της συσκευής;"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Εκχώρηση πρόσβασης στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> σε περισσότερες φωτογραφίες;"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η εγγραφή ήχου;"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Αυτή η εφαρμογή θα μπορεί να εγγράφει ήχο μόνο όταν τη χρησιμοποιείτε"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η εγγραφή ήχου;"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Να εμφανίζεται ένα μήνυμα όταν οι εφαρμογές αποκτούν πρόσβαση σε κείμενο, εικόνες ή άλλο περιεχόμενο που έχετε αντιγράψει"</string> <string name="show_password_title" msgid="2877269286984684659">"Εμφάνιση κωδικών πρόσβασης"</string> <string name="show_password_summary" msgid="1110166488865981610">"Σύντομη εμφάνιση χαρακτήρων κατά την πληκτρολόγηση"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Αυτή η εφαρμογή δηλώνει ότι ενδέχεται να κοινοποιεί δεδομένα που αφορούν την άδεια \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\" σε τρίτα μέρη"</string> </resources> diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml index 77ddf5f41..787452e89 100644 --- a/PermissionController/res/values-en-rAU/strings.xml +++ b/PermissionController/res/values-en-rAU/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# day}other{# days}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# mins}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}other{# secs}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Any permission"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Any time"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Last 7 days"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Last 24 hours"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Last 1 hour"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Last 15 minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Last 1 minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Last # day}other{Last # days}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Last # hour}other{Last # hours}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Last # minute}other{Last # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No permission usages"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Most recent access at any time"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Most recent access in last 7 days"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Permission usage in last 1 hour"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Permission usage in last 15 minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Permission usage in last 1 minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Not used in past 24 hours"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Not used in past 7 days"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Not used in past # day}other{Not used in past # days}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Not used in past # hour}other{Not used in past # hours}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Used by 1 app}other{Used by # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"See all in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtered by: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Not allowed"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 second}other{# seconds}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# second}other{# seconds}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Permission reminders"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 unused app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access <b>photos, videos, music, audio and other files</b> on this device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access music and audio on this device?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access photos and videos on this device?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> access to more photos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string> <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string> <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string> </resources> diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml index eb723454e..7f618a719 100644 --- a/PermissionController/res/values-en-rCA/strings.xml +++ b/PermissionController/res/values-en-rCA/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep “While the app is in use”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep “Only this time”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# day}other{# days}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# mins}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}other{# secs}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Any permission"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Any time"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Last 7 days"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Last 24 hours"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Last 1 hour"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Last 15 minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Last 1 minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Last # day}other{Last # days}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Last # hour}other{Last # hours}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Last # minute}other{Last # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No permission usages"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Most recent access at any time"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Most recent access in last 7 days"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Permission usage in last 1 hour"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Permission usage in last 15 minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Permission usage in last 1 minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Not used in past 24 hours"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Not used in past 7 days"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Not used in past # day}other{Not used in past # days}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Not used in past # hour}other{Not used in past # hours}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Used by 1 app}other{Used by # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"See all in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtered by: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Don’t allow"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Not allowed"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 second}other{# seconds}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# second}other{# seconds}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Permission reminders"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 unused app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access <b>photos, videos, music, audio, and other files</b> on this device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access music and audio on this device?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access photos and videos on this device?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> access to more photos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images, or other content you’ve copied"</string> <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string> <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string> </resources> diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml index ebd914609..f6091d7e9 100644 --- a/PermissionController/res/values-en-rGB/strings.xml +++ b/PermissionController/res/values-en-rGB/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# day}other{# days}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# mins}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}other{# secs}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Any permission"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Any time"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Last 7 days"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Last 24 hours"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Last 1 hour"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Last 15 minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Last 1 minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Last # day}other{Last # days}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Last # hour}other{Last # hours}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Last # minute}other{Last # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No permission usages"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Most recent access at any time"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Most recent access in last 7 days"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Permission usage in last 1 hour"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Permission usage in last 15 minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Permission usage in last 1 minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Not used in past 24 hours"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Not used in past 7 days"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Not used in past # day}other{Not used in past # days}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Not used in past # hour}other{Not used in past # hours}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Used by 1 app}other{Used by # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"See all in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtered by: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Not allowed"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 second}other{# seconds}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# second}other{# seconds}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Permission reminders"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 unused app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access <b>photos, videos, music, audio and other files</b> on this device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access music and audio on this device?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access photos and videos on this device?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> access to more photos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string> <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string> <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string> </resources> diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml index ebd914609..f6091d7e9 100644 --- a/PermissionController/res/values-en-rIN/strings.xml +++ b/PermissionController/res/values-en-rIN/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# day}other{# days}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# mins}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}other{# secs}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Any permission"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Any time"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Last 7 days"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Last 24 hours"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Last 1 hour"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Last 15 minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Last 1 minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Last # day}other{Last # days}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Last # hour}other{Last # hours}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Last # minute}other{Last # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No permission usages"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Most recent access at any time"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Most recent access in last 7 days"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Permission usage in last 1 hour"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Permission usage in last 15 minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Permission usage in last 1 minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Not used in past 24 hours"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Not used in past 7 days"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Not used in past # day}other{Not used in past # days}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Not used in past # hour}other{Not used in past # hours}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Used by 1 app}other{Used by # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"See all in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtered by: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Not allowed"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 second}other{# seconds}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# second}other{# seconds}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Permission reminders"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 unused app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access <b>photos, videos, music, audio and other files</b> on this device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access music and audio on this device?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access photos and videos on this device?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> access to more photos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string> <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string> <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string> </resources> diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml index b78542f94..0ee27876c 100644 --- a/PermissionController/res/values-en-rXC/strings.xml +++ b/PermissionController/res/values-en-rXC/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep “While the app is in use”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep “Only this time”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# day}other{# days}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# mins}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}other{# secs}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Any permission"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Any time"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Last 7 days"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Last 24 hours"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Last 1 hour"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Last 15 minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Last 1 minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Last # day}other{Last # days}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Last # hour}other{Last # hours}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Last # minute}other{Last # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No permission usages"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Most recent access at any time"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Most recent access in last 7 days"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Permission usage in last 1 hour"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Permission usage in last 15 minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Permission usage in last 1 minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Not used in past 24 hours"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Not used in past 7 days"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Not used in past # day}other{Not used in past # days}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Not used in past # hour}other{Not used in past # hours}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Used by 1 app}other{Used by # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"See all in Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtered by: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Don’t allow"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Not allowed"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hour}other{# hours}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 second}other{# seconds}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# second}other{# seconds}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Permission reminders"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 unused app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access <b>photos, videos, music, audio, and other files</b> on this device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access music and audio on this device?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access photos and videos on this device?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> access to more photos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to record audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images, or other content you’ve copied"</string> <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string> <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string> </resources> diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml index ead99b5eb..21ff83ac9 100644 --- a/PermissionController/res/values-es-rUS/strings.xml +++ b/PermissionController/res/values-es-rUS/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener en \"Mientras la app está en uso\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantener \"Solo esta vez\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir el acceso a todas las fotos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar más fotos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionar más fotos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permitir igualmente"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignorar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 día}many{# días}other{# días}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# día}many{# días}other{# días}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Cualquier permiso"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Cualquier momento"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 días"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Último día (#)}many{Últimos # días}other{Últimos # días}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora (#)}many{Últimas # horas}other{Últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto (#)}many{Últimos # minutos}other{Últimos # minutos}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ningún uso de permisos"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acceso más reciente en cualquier momento"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acceso más reciente en los últimos 7 días"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso de permisos en la última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso de permisos en los últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso de permisos en el último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Sin uso en las últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"No se usó en los últimos 7 días"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{No se usó en el último día (#)}many{No se usó en los últimos # días}other{No se usó en los últimos # días}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{No se usó en la última hora (#)}many{No se usó en las últimas # horas}other{No se usó en las últimas # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{En uso por parte de 1 app}many{En uso por parte de # apps}other{En uso por parte de # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver todo en el panel"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrado por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Solo permitir acceso al contenido multimedia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir todo el tiempo"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir solo con la app en uso"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas las fotos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir las fotos seleccionadas"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Sin permiso"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más apps que pueden acceder a todos los archivos"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# días}other{# días}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}many{# minutos}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}many{# segundos}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Recordatorios de permisos"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app en desuso"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps que no usas"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a <b>fotos, videos, música, audio y otros archivos</b> del dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a la música y los archivos de audio de este dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a las fotos y los videos de este dispositivo?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"¿Quieres que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pueda acceder a más fotos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grabe audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La app solo podrá grabar audio cuando esté en uso"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grabe audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar un mensaje cuando las apps accedan a textos, imágenes y otro contenido que hayas copiado"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar contraseñas"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostrar caracteres brevemente mientras escribes"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta app indicó que podría compartir datos de <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terceros"</string> </resources> diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml index 9cf2c1787..32f8ca108 100644 --- a/PermissionController/res/values-es/strings.xml +++ b/PermissionController/res/values-es/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener \"Mientras la aplicación se esté usando\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantener \"Solo esta vez\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acceso a todas las fotos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar más fotos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionar más fotos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permitir de todas formas"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Cerrar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 día}many{# días}other{# días}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# día}many{# días}other{# días}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Cualquier permiso"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Cualquier fecha"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 días"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Último día}many{Últimos # días}other{Últimos # días}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora}many{Últimas # horas}other{Últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto}many{Últimos # minutos}other{Últimos # minutos}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"No se han usado los permisos"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acceso más reciente en cualquier momento"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acceso más reciente en los últimos 7 días"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso de permisos en la última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso de permisos en los últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso de permisos en el último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"No se ha usado en las últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"No se ha usado en los últimos 7 días"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{No se ha usado en el último día}many{No se ha usado en los últimos # días}other{No se ha usado en los últimos # días}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{No se ha usado en la última hora}many{No se ha usado en las últimas # horas}other{No se ha usado en las últimas # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Usado por 1 aplicación}many{Usado por # aplicaciones}other{Usado por # aplicaciones}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver todo en el panel de control"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrados por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acceso solo al contenido multimedia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir siempre"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir solo mientras se usa la aplicación"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas la fotos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir fotos seleccionadas"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"No permitido"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más aplicaciones que pueden acceder a todos los archivos"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# días}other{# días}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}many{# minutos}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}many{# segundos}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Recordatorios de permisos"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplicación no usada"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplicaciones no usadas"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a <b>fotos, vídeos, música, audio y otros archivos</b> del dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a música y audio de este dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a fotos y vídeos de este dispositivo?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"¿Quieres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a más fotos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grabe audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La aplicación solo podrá grabar audio mientras la estés usando."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grabe audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Muestra un mensaje cuando las aplicaciones acceden a texto, imágenes u otro contenido que has copiado"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar contraseñas"</string> <string name="show_password_summary" msgid="1110166488865981610">"Muestra los caracteres brevemente mientras escribes"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta aplicación ha indicado que es posible que comparta datos de <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terceros"</string> </resources> diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml index e46ce49ca..0fcb63673 100644 --- a/PermissionController/res/values-et/strings.xml +++ b/PermissionController/res/values-et/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Säilita valik „Rakenduse kasutamise ajal”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Säilita ainult sel korral"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisateave"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Luba juurdepääs kõikidele fotodele"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vali fotod"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Valige veel fotosid"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ära vali rohkem fotosid"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ära luba ikkagi"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Loobu"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 päev}other{# päeva}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 tund}other{# tundi}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# päev}other{# päeva}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# tund}other{# tundi}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Mis tahes luba"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Mis tahes ajal"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Viimased seitse päeva"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Viimased 24 tundi"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Viimane tund"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Viimased 15 minutit"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Viimane 1 minut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Viimane päev}other{Viimased # päeva}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Viimane tund}other{Viimased # tundi}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Viimane minut}other{Viimased # minutit}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Lube pole kasutatud"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Hiljutisim juurdepääs mis tahes ajal"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Hiljutisim juurdepääs viimase 7 päeva jooksul"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Lubade kasutus viimase 1 tunni jooksul"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Lubade kasutus viimase 15 minuti jooksul"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Lubade kasutus viimase 1 minuti jooksul"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Pole viimase 24 tunni jooksul kasutatud"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Pole viimase 7 päeva jooksul kasutatud"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Pole viimase # päeva jooksul kasutatud}other{Pole viimase # päeva jooksul kasutatud}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Pole viimase # tunni jooksul kasutatud}other{Pole viimase # tunni jooksul kasutatud}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Kasutab 1 rakendus}other{Kasutavad # rakendust}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Kuva kõik juhtpaneelil"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtreerimisalus: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Luba juurdepääs ainult meediale"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Luba alati"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Luba rakenduse kasutamise ajal"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Luba kõik fotod"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Luba valitud fotod"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Küsi iga kord"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ära luba"</string> <string name="precise_image_description" msgid="6349638632303619872">"Täpne asukoht"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Pole lubatud"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Kuva rohkem rakendusi, mis kõigile failidele juurde pääsevad"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 päev}other{# päeva}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 tund}other{# tundi}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minut}other{# minutit}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekund}other{# sekundit}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# tund}other{# tundi}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}other{# minutit}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekund}other{# sekundit}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Loa meeldetuletused"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 kasutamata rakendus"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> kasutamata rakendust"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Anda rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> juurdep. <b>foto-, video-, muusika-, heli- ja muudele failidele</b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Kas anda rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> selles seadmes juurdepääs muusikale ja helifailidele?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Kas anda rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> selles seadmes juurdepääs fotodele ja videotele?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Kas anda rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> juurdepääs rohkematele fotodele?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Kas lubada rakendusel <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> salvestada heli?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Rakendus saab heli salvestada vaid siis, kui rakendust kasutate"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Kas lubada rakendusel <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> heli salvestada?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Kui rakendused pääsevad juurde kopeeritud tekstile, piltidele või muule sisule, kuvatakse teade"</string> <string name="show_password_title" msgid="2877269286984684659">"Kuva paroolid"</string> <string name="show_password_summary" msgid="1110166488865981610">"Sisestamisel kuvatakse hetkeks tähemärgid"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"See rakendus andis teada, et võib loa <xliff:g id="PERMISSION_NAME">%s</xliff:g> andmeid jagada kolmandate osapooltega"</string> </resources> diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml index 8eb01a0b0..4b8abdbff 100644 --- a/PermissionController/res/values-eu/strings.xml +++ b/PermissionController/res/values-eu/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantendu “Aplikazioa abian denean” aukera"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantendu \"Oraingo honetan soilik\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Datu gehiago"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ez eman baimenik halere"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Baztertu"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 egun}other{# egun}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ordu}other{# ordu}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# egun}other{# egun}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ordu}other{# ordu}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Edozein baimen"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Edonoiz"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Azken 7 egunetan"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Azken 24 orduetan"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Azken orduan"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Azken 15 minutuetan"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Azken minutuan"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Azken # egunean}other{Azken # egunetan}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Azken # orduan}other{Azken # orduetan}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Azken # minutuan}other{Azken # minutuetan}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ez da eskatu baimenik"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Orain arteko azken sarbidea"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Azken zazpi egunetako azken sarbidea"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Azken ordubetean baimenei eman zaien erabilera"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Azken 15 minutuetan baimenei eman zaien erabilera"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Azken minutuan baimenei eman zaien erabilera"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ez da erabili azken 24 orduetan"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Ez da erabili azken zazpi egunetan"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Ez da erabili azken # egunean}other{Ez da erabili azken # egunetan}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Ez da erabili azken # orduan}other{Ez da erabili azken # orduetan}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Aplikazio batek erabili du}other{# aplikaziok erabili dute}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ikusi guztiak panelean"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Iragazteko irizpidea: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Eman multimedia-fitxategiak soilik atzitzeko baimena"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Eman baimena beti"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Aplikazioa erabiltzean soilik"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"Galdetu beti"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ez eman baimenik"</string> <string name="precise_image_description" msgid="6349638632303619872">"Kokapen zehatza"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"Baimendu gabe"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ikusi fitxategi guztiak atzi ditzaketen aplikazio gehiago"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 egun}other{# egun}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ordu}other{# ordu}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minutu}other{# minutu}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}other{# segundo}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ordu}other{# ordu}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minutu}other{# minutu}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}other{# segundo}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Baimenen abisuak"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Erabiltzen ez den 1 aplikazio"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Erabiltzen ez diren <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplikazio"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Gailuko <b>argazkiak, bideoak, musika, audioa eta bestelako fitxategiak</b> atzitzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Gailuko musika eta audioa atzitzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Gailuko argazkiak eta bideoak atzitzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Audioa grabatzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikazioak hura erabiltzean soilik grabatuko du audioa"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Audioa grabatzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Erakutsi mezu bat aplikazio batek kopiatu dituzun testuak, irudiak edo edukiak atzitzen dituenean"</string> <string name="show_password_title" msgid="2877269286984684659">"Erakutsi pasahitzak"</string> <string name="show_password_summary" msgid="1110166488865981610">"Idatzi ahala, erakutsi karaktereak laburki"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml index c0a896418..9e1d0c700 100644 --- a/PermissionController/res/values-fa/strings.xml +++ b/PermissionController/res/values-fa/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"تغییر ندادن اجازه «هنگامی که از برنامه استفاده میشود»"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"«فقط این بار» نگه داشته شود"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"اطلاعات بیشتر"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"مجاز کردن دسترسی به همه عکسها"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"انتخاب عکس مجاز است"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"انتخاب عکسهای بیشتر مجاز است"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"انتخاب عکسهای بیشتر مجاز نیست"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"درهرصورت اجازه نیست"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"رد کردن"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> مجوز از <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> مجوز"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{یک روز}one{# روز}other{# روز}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{یک ساعت}one{# ساعت}other{# ساعت}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{یک دقیقه}one{# دقیقه}other{# دقیقه}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{یک ثانیه}one{# ثانیه}other{# ثانیه}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# روز}one{# روز}other{# روز}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ساعت}one{# ساعت}other{# ساعت}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# دقیقه}one{# دقیقه}other{# دقیقه}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ثانیه}one{# ثانیه}other{# ثانیه}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"همه مجوزها"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"هر زمانی"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"۷ روز گذشته"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"۲۴ ساعت اخیر"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"۱ ساعت اخیر"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"۱۵ دقیقه اخیر"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"۱ دقیقه اخیر"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# روز گذشته}one{# روز گذشته}other{# روز گذشته}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ساعت گذشته}one{# ساعت گذشته}other{# ساعت گذشته}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# دقیقه گذشته}one{# دقیقه گذشته}other{# دقیقه گذشته}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"هیچ مجوزی استفاده نشده است"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"آخرین دسترسی در هرزمانی"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"آخرین دسترسی در ۷ روز گذشته"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"پراستفادهترین مجوزها در ۱ ساعت گذشته"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"پراستفادهترین مجوزها در ۱۵ دقیقه گذشته"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"پراستفادهترین مجوزها در ۱ دقیقه گذشته"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"در ۲۴ ساعت گذشته استفاده نشده است"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"در ۷ روز گذشته استفاده نشده است"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{در # روز گذشته استفاده نشده است}one{در # روز گذشته استفاده نشده است}other{در # روز گذشته استفاده نشده است}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{در # ساعت گذشته استفاده نشده است}one{در # ساعت گذشته استفاده نشده است}other{در # ساعت گذشته استفاده نشده است}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{یک برنامه از آن استفاده کرده است}one{# برنامه از آن استفاده کرده است}other{# برنامه از آن استفاده کرده است}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"دیدن همه موارد در داشبورد"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"فیلترشده براساس: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"اجازه دادن فقط برای دسترسی به رسانهها"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"همیشه مجاز است"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"فقط هنگام استفاده از برنامه مجاز است"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"مجاز کردن همه عکسها"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"مجاز کردن عکسهای منتخب"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"هربار پرسیده شود"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"اجازه ندادن"</string> <string name="precise_image_description" msgid="6349638632303619872">"مکان دقیق"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"اجازه ندادن"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"دیدن برنامههای دیگری که به همه فایلها دسترسی دارند"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{یک روز}one{# روز}other{# روز}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{یک ساعت}one{# ساعت}other{# ساعت}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{یک دقیقه}one{# دقیقه}other{# دقیقه}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{یک ثانیه}one{# ثانیه}other{# ثانیه}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ساعت}one{# ساعت}other{# ساعت}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# دقیقه}one{# دقیقه}other{# دقیقه}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ثانیه}one{# ثانیه}other{# ثانیه}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"یادآوریهای مجوز"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"۱ برنامه استفادهنشده"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> برنامه استفادهنشده"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه میدهید به <b>عکسها، ویدیوها، موسیقی، صوت، و فایلهای دیگر</b> این دستگاه دسترسی داشته باشد؟"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه میدهید به فایلهای موسیقی و صوتی در این دستگاه دسترسی داشته باشد؟"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه میدهید به عکسها و ویدیوهای این دستگاه دسترسی داشته باشد؟"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه میدهید به عکسهای بیشتری دسترسی داشته باشد؟"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"به </b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود صدا ضبط کند؟"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"این برنامه فقط وقتی از آن استفاده میکنید، میتواند صدا ضبط کند"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"به </b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود صدا ضبط کند؟"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"وقتی برنامهها به نوشتار، تصویر، یا محتوای دیگری که کپی کردهاید دسترسی پیدا میکنند، پیامی نشان داده میشود"</string> <string name="show_password_title" msgid="2877269286984684659">"نمایش گذرواژهها"</string> <string name="show_password_summary" msgid="1110166488865981610">"همانطور که تایپ میکنید، نویسهها را برای مدت کوتاهی نشان میدهد"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"این برنامه بیان کرده است که ممکن است دادههای <xliff:g id="PERMISSION_NAME">%s</xliff:g> را با اشخاص ثالث همرسانی کند"</string> </resources> diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml index d1435c1d6..73cb8e95f 100644 --- a/PermissionController/res/values-fi/strings.xml +++ b/PermissionController/res/values-fi/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Pidä \"Kun sovellusta käytetään\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Säilytä \"Vain tällä kertaa\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisätietoja"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Salli pääsy kaikkiin kuviin"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Valitse kuvat"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Valitse lisää kuvia"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Älä valitse enempää kuvia"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Älä salli silti"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ohita"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 päivä}other{# päivää}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 tunti}other{# tuntia}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# päivä}other{# päivää}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# tunti}other{# tuntia}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Kaikki käyttöoikeudet"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Milloin tahansa"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Viimeiset 7 päivää"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Viimeiset 24 tuntia"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Viimeisin tunti"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Viimeiset 15 minuuttia"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Viimeinen minuutti"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Edellinen päivä}other{Viimeiset # päivää}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Viimeisin tunti}other{Viimeiset # tuntia}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Viimeinen minuutti}other{Viimeiset # minuuttia}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Käyttöoikeuksia ei käytetty"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Viimeksi käytetyt koska tahansa"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Viimeksi käytetyt viimeisten 7 päivän ajalta"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Lupien käyttö viimeisen tunnin ajalta"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Lupien käyttö viimeisten 15 min ajalta"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Lupien käyttö viimeisen minuutin ajalta"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ei käytetty 24 viime tunnin aikana"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Ei käytetty 7 viime päivän aikana"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Ei käytetty viimeisen päivän aikana}other{Ei käytetty # viime päivän aikana}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Ei käytetty viimeisen tunnin aikana}other{Ei käytetty # viime tunnin aikana}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 sovelluksen käyttämä}other{# sovelluksen käyttämä}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Näytä kaikki ohjauspaneelissa"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Suodatusperuste: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Salli pääsy vain mediaan"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Salli aina"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Salli vain, kun sovellus on käytössä"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Salli kaikki kuvat"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Salli valitut kuvat"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Kysy aina"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Älä salli"</string> <string name="precise_image_description" msgid="6349638632303619872">"Tarkka sijainti"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ei sallittu"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Muut sovellukset, joilla on pääsy kaikkiin tiedostoihin"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 päivä}other{# päivää}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 tunti}other{# tuntia}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuutti}other{# minuuttia}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunti}other{# sekuntia}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# tunti}other{# tuntia}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuutti}other{# minuuttia}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunti}other{# sekuntia}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Käyttölupamuistutukset"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 käyttämätön sovellus"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> käyttämätöntä sovellusta"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pääsyn <b>kuviin, videoihin, musiikkiin, audioon ja muihin tiedostoihin</b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pääsyn tällä laitteella oleviin musiikki- ja audiotiedostoihin?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pääsyn laitteella oleviin kuviin ja mediaan?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pääsyn useampiin kuviin?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> nauhoittaa audiota?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Sovellus voi tallentaa audiota vain silloin, kun käytät sitä"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tallentaa audiota?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Näytä viesti, kun sovellukset käyttävät kopioimaasi tekstiä, kuvia tai muuta sisältöä"</string> <string name="show_password_title" msgid="2877269286984684659">"Näytä salasanat"</string> <string name="show_password_summary" msgid="1110166488865981610">"Näytä kirjaimet hetkellisesti, kun kirjoitat"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Sovellus on ilmoittanut, että se saattaa jakaa <xliff:g id="PERMISSION_NAME">%s</xliff:g>dataa kolmansille osapuolille"</string> </resources> diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml index 76e412a1f..01b8dc19b 100644 --- a/PermissionController/res/values-fr-rCA/strings.xml +++ b/PermissionController/res/values-fr-rCA/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder « Pendant l\'utilisation de l\'appli »"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Garder « Uniquement cette fois »"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"En savoir plus"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Autoriser l\'accès à toutes les photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Sélectionner des photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Sélectionner plus de photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne pas sélectionner plus de photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne pas autoriser quand même"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Fermer"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 jour}one{# jour}many{# jours}other{# jours}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 heure}one{# heure}many{# heures}other{# heures}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}many{# mins}other{# mins}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}one{# sec}many{# secs}other{# secs}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# jour}one{# jour}many{# de jours}other{# jours}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# heure}one{# heure}many{# d\'heures}other{# heures}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}many{# de minutes}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}many{# de secondes}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Toute autorisation"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"À tout moment"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Les 7 derniers jours"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Les 24 dernières heures"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"La dernière heure"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Les 15 dernières minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Dernière minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Depuis # jour}one{Depuis # jour}many{Depuis # de jours}other{Depuis # jours}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Depuis # heure}one{Depuis # heure}many{Depuis # d\'heures}other{Depuis # heures}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Depuis # minute}one{Depuis # minute}many{Depuis # de minutes}other{Depuis # minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Aucune autoris. d\'utilisation"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"L\'accès le plus récent en tout temps"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Accès le plus récent au cours des 7 derniers jours"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Utilisation des autorisat. dans la dernière heure"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Utilisation des autor. dans les 15 dern. minutes"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Utilisation des autorisat. dans la dernière minute"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Aucune utilisation au cours des dernières 24 heures"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Inutilisée au cours des 7 derniers jours"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Aucune utilisation depuis # jour}one{Aucune utilisation depuis # jour}many{Aucune utilisation depuis # de jours}other{Aucune utilisation depuis # jours}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Aucune utilisation depuis # heure}one{Aucune utilisation depuis # heure}many{Aucune utilisation depuis # d\'heures}other{Aucune utilisation depuis # heures}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilisation : 1 application}one{Utilisation : # application}many{Utilisation : # applications}other{Utilisation : # applications}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Tout afficher dans le tableau de bord"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtré par : <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Autoriser à accéder aux éléments multimédias seulement"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Toujours autoriser"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser uniquement lorsque l\'appli est en cours d\'utilisation"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Autoriser toutes les photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Autoriser les photos sélectionnées"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Toujours demander"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string> <string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Non autorisées"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Afficher d\'autres applis pouvant accéder à tous les fichiers"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 jour}one{# jour}many{# jours}other{# jours}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 heure}one{# heure}many{# heures}other{# heures}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}one{# minute}many{# minutes}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 seconde}one{# seconde}many{# secondes}other{# secondes}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# heure}one{# heure}many{# d\'heures}other{# heures}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}one{# minute}many{# de minutes}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# seconde}one{# seconde}many{# de secondes}other{# secondes}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Rappels d\'autorisation"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 application non utilisée"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications non utilisées"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder <b>aux photos, aux vidéos, et aux fichiers musicaux, audio et autres</b> sur cet appareil?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux fichiers musicaux et audio sur cet appareil?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux photos et aux vidéos sur cet appareil?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Autoriser l\'accès à plus de photos à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à enregistrer l\'audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'application pourra uniquement enregistrer de l\'audio lorsque vous l\'utilisez"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à enregistrer de l\'audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afficher un message lorsque les applications accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string> <string name="show_password_title" msgid="2877269286984684659">"Afficher les mots de passe"</string> <string name="show_password_summary" msgid="1110166488865981610">"Afficher les caractères brièvement pendant la saisie"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Cette application a indiqué qu\'elle pourrait partager des données de <xliff:g id="PERMISSION_NAME">%s</xliff:g> avec des tiers."</string> </resources> diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml index 2f01f9786..03609e695 100644 --- a/PermissionController/res/values-fr/strings.xml +++ b/PermissionController/res/values-fr/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder \"Seulement quand l\'appli est en cours d\'utilisation\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Conserver le paramètre \"Uniquement cette fois-ci\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Plus d\'infos"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Autoriser l\'accès à toutes les photos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Sélectionner des photos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Sélectionner d\'autres photos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne pas sélectionner d\'autres photos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne pas autoriser"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Fermer"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 jour}one{# jour}many{# jours}other{# jours}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 heure}one{# heure}many{# heures}other{# heures}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}one{# s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# jour}one{# jour}many{# jours}other{# jours}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Toute autorisation"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Indifférent"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 derniers jours"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Dernières 24 heures"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Dernière heure"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 dernières minutes"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Dernière minute"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Dernier jour (#)}one{Dernier jour (#)}many{# derniers jours}other{# derniers jours}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Dernière heure (#)}one{Dernière heure (#)}many{# dernières heures}other{# dernières heures}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Dernière minute (#)}one{Dernière minute (#)}many{# dernières minutes}other{# dernières minutes}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Aucune autorisation utilisée"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Tous les accès les plus récents"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Accès les plus récents (7 derniers jours)"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Autorisations utilisées (dernière heure)"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Autorisations utilisées (15 dernières minutes)"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Autorisations utilisées (dernière minute)"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Aucune utilisation au cours des dernières 24 heures"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Aucune utilisation au cours des 7 derniers jours"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Non utilisée au cours du dernier jour (#)}one{Non utilisée au cours du dernier jour (#)}many{Non utilisée au cours des # derniers jours}other{Non utilisée au cours des # derniers jours}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Non utilisée au cours de la dernière heure (#)}one{Non utilisée au cours de la dernière heure (#)}many{Non utilisée au cours des # dernières heures}other{Non utilisée au cours des # dernières heures}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilisation par 1 appli}one{Utilisation par # appli}many{Utilisation par # applis}other{Utilisation par # applis}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Tout afficher dans le tableau de bord"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Données filtrées par : <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Autoriser l\'accès aux fichiers multimédias uniquement"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Toujours autoriser"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser seulement si l\'appli est en cours d\'utilisation"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Autoriser toutes les photos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Autoriser les photos sélectionnées"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Toujours demander"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string> <string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Non autorisées"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Voir plus d\'applis pouvant accéder à tous les fichiers"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 jour}one{# jour}many{# jours}other{# jours}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 heure}one{# heure}many{# heures}other{# heures}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minute}one{# minute}many{# minutes}other{# minutes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 seconde}one{# seconde}many{# secondes}other{# secondes}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minute}one{# minute}many{# minutes}other{# minutes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# seconde}one{# seconde}many{# secondes}other{# secondes}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Rappels relatifs aux autorisations"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 application inutilisée"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications inutilisées"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux <b>photos, vidéos, fichiers musicaux/audio, etc.</b> sur l\'appareil ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la musique et à l\'audio sur cet appareil ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux photos et vidéos sur cet appareil ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à plus de photos ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à enregistrer de l\'audio ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Cette application ne pourra réaliser des enregistrements audio que lorsque vous l\'utiliserez"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> de réaliser des enregistrements audio ?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afficher un message lorsque les applis accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string> <string name="show_password_title" msgid="2877269286984684659">"Afficher les mots de passe"</string> <string name="show_password_summary" msgid="1110166488865981610">"Afficher brièvement les caractères pendant la saisie"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Cette appli a indiqué qu\'elle peut partager des données de <xliff:g id="PERMISSION_NAME">%s</xliff:g> avec des tiers"</string> </resources> diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml index 5692c2710..14afe1fb5 100644 --- a/PermissionController/res/values-gl/strings.xml +++ b/PermissionController/res/values-gl/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter mentres se estea utilizando a aplicación"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter Só esta vez"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Máis datos"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acceso a todas as fotos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar máis fotos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Non seleccionar máis fotos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Non permitir aínda así"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Pechar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 día}other{# días}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# día}other{# días}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Calquera permiso"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"En calquera momento"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 días"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Últimos día}other{Últimos # días}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora}other{Últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto}other{Últimos # minutos}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Non se utilizaron os permisos"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acceso máis recente en todo o tempo"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acceso máis recente nos últimos 7 días"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso dos permisos durante a última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso dos permisos durante os últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso dos permisos durante o último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Non se utilizou nas últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Non se utilizou nos últimos 7 días"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Non se utilizou no último día}other{Non se utilizou nos últimos # días}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Non se utilizou na última hora}other{Non se utilizou nas últimas # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Permiso usado por 1 aplicación}other{Permiso usado por # aplicacións}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver todo no panel de control"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Vista filtrada por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acceso só a ficheiros multimedia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir sempre"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir só mentres se use a aplicación"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas as fotos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir fotos seleccionadas"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar sempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Non permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Localización precisa"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Permiso non concedido"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver máis aplicacións con acceso a todos os ficheiros"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}other{# días}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Recordatorios de permisos"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplicación que non se usa"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplicacións que non se usan"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Queres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda ás <b>fotos, vídeos, música, audio e outros ficheiros</b> do dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Queres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda á música e aos ficheiros de audio deste dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Queres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda ás fotos e aos vídeos deste dispositivo?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Queres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a máis fotos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Esta aplicación só poderá gravar audio cando a esteas utilizando"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostra unha mensaxe cando as aplicacións acceden ao texto, ás imaxes ou ao contido que copiaches"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar contrasinais"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostra os caracteres brevemente mentres escribes"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"A aplicación indicou que é posible que comparta con terceiros os seguintes datos: <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string> </resources> diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml index 76fd4affc..1b55eef2a 100644 --- a/PermissionController/res/values-gu/strings.xml +++ b/PermissionController/res/values-gu/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ઍપ ઉપયોગમાં હોય ત્યારે” આ રાખો"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ફક્ત આ વખતે” રાખો"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"વધુ માહિતી"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"કોઈપણ રીતે મંજૂરી આપશો નહીં"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"છોડી દો"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> માંથી <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 દિવસ}one{# દિવસ}other{# દિવસ}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 કલાક}one{# કલાક}other{# કલાક}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 મિનિટ}one{# મિનિટ}other{# મિનિટ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 સેકન્ડ}one{# સેકન્ડ}other{# સેકન્ડ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# દિવસ}one{# દિવસ}other{# દિવસ}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# કલાક}one{# કલાક}other{# કલાક}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# મિનિટ}one{# મિનિટ}other{# મિનિટ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# સેકન્ડ}one{# સેકન્ડ}other{# સેકન્ડ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"કોઈપણ પરવાનગી"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ગમે ત્યારે"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"છેલ્લા 7 દિવસ"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"છેલ્લા 24 કલાક"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"છેલ્લો 1 કલાક"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"છેલ્લી 15 મિનિટ"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"છેલ્લી 1 મિનિટ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{છેલ્લો # દિવસ}one{છેલ્લો # દિવસ}other{છેલ્લા # દિવસ}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{છેલ્લો # કલાક}one{છેલ્લો # કલાક}other{છેલ્લા # કલાક}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{છેલ્લી # મિનિટ}one{છેલ્લી # મિનિટ}other{છેલ્લી # મિનિટ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"પરવાનગીનો ઉપયોગ થયો નથી"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"કોઈ પણ સમયે સૌથી તાજેતરનો ઍક્સેસ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"છેલ્લા 7 દિવસમાં સૌથી તાજેતરનો ઍક્સેસ"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"છેલ્લા 1 કલાકમાં પરવાનગીનો વપરાશ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"છેલ્લી 15 મિનિટમાં પરવાનગીનો ઉપયોગ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"છેલ્લી 1 મિનિટમાં પરવાનગીનો ઉપયોગ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"છેલ્લા 24 કલાકમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"છેલ્લા 7 દિવસમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{છેલ્લા # દિવસમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}one{છેલ્લા # દિવસમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}other{છેલ્લા # દિવસમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{છેલ્લા # કલાકમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}one{છેલ્લા # કલાકમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}other{છેલ્લા # કલાકમાં કોઈ ઉપયોગ કરવામાં આવ્યો નથી}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ઍપ દ્વારા ઉપયોગ કરવામાં આવ્યો}one{# ઍપ દ્વારા ઉપયોગ કરવામાં આવ્યો}other{# ઍપ દ્વારા ઉપયોગ કરવામાં આવ્યો}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"બધી વિગતો ડૅશબોર્ડમાં જુઓ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"આના અનુસાર ફિલ્ટર કર્યું: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"માત્ર મીડિયાના ઍક્સેસની મંજૂરી આપો"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"હંમેશાં મંજૂરી આપો"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ઍપનો ઉપયોગ કરતી વખતે જ મંજૂરી આપો"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"દર વખતે પૂછો"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"મંજૂરી આપશો નહીં"</string> <string name="precise_image_description" msgid="6349638632303619872">"ચોક્કસ સ્થાન"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"મંજૂરી નથી"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"બધી ફાઇલોને ઍક્સેસ કરી શકે તેવી વધુ ઍપ જુઓ"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 દિવસ}one{# દિવસ}other{# દિવસ}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 કલાક}one{# કલાક}other{# કલાક}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 મિનિટ}one{# મિનિટ}other{# મિનિટ}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 સેકન્ડ}one{# સેકન્ડ}other{# સેકન્ડ}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# કલાક}one{# કલાક}other{# કલાક}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# મિનિટ}one{# મિનિટ}other{# મિનિટ}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# સેકન્ડ}one{# સેકન્ડ}other{# સેકન્ડ}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"મંજૂરીના રિમાઇન્ડર"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ન વપરાયેલી ઍપ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ન વપરાયેલી ઍપ"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને આ ડિવાઇસ પર <b>ફોટા, વીડિયો, મ્યુઝિક, ઑડિયો અને અન્ય ફાઇલો<b>ના ઍક્સેસની મંજૂરી આપીએ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"શું <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને આ ડિવાઇસ પરની મ્યુઝિક અને ઑડિયો ફાઇલો ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"શું <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને આ ડિવાઇસ પરના ફોટા અને વીડિયો ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને ઑડિયો રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો, માત્ર ત્યારે જ ઍપ ઑડિયો રેકોર્ડ કરી શકશે"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને ઑડિયો રેકોર્ડ કરવાની મંજૂરી આપીએ?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"જ્યારે ઍપ તમે કૉપિ કરેલી ટેક્સ્ટ, છબીઓ કે અન્ય કન્ટેન્ટનો ઍક્સેસ કરે, ત્યારે મેસેજ બતાવો"</string> <string name="show_password_title" msgid="2877269286984684659">"પાસવર્ડ બતાવો"</string> <string name="show_password_summary" msgid="1110166488865981610">"તમે ટાઇપ કરો ત્યારે થોડા સમય માટે અક્ષરો બતાવો"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml index 04e7b5c8c..815001bad 100644 --- a/PermissionController/res/values-hi/strings.xml +++ b/PermissionController/res/values-hi/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"ऐप्लिकेशन इस्तेमाल करते समय\" अनुमति बनाए रखें"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“सिर्फ़ इस बार अनुमति दें” को बनाए रखें"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ज़्यादा जानकारी"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"फिर भी अनुमति न दें"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"खारिज करें"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> में से <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 दिन}one{# दिन}other{# दिन}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 घंटा}one{# घंटा}other{# घंटे}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 मिनट}one{# मिनट}other{# मिनट}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 सेकंड}one{# सेकंड}other{# सेकंड}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# दिन}one{# दिन}other{# दिन}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# घंटा}one{# घंटा}other{# घंटे}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# मिनट}one{# मिनट}other{# मिनट}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# सेकंड}one{# सेकंड}other{# सेकंड}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"कोई भी अनुमति"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"किसी भी समय"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"पिछले सात दिनों में"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"पिछले 24 घंटों में"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"पिछले एक घंटे में"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"पिछले 15 मिनट में"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"आखिरी एक मिनट"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{पिछला # दिन}one{पिछला # दिन}other{पिछले # दिन}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{पिछला # घंटा}one{पिछला # घंटा}other{पिछले # घंटे}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{पिछला # मिनट}one{पिछला # मिनट}other{पिछले # मिनट}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"अनुमति का इस्तेमाल नहीं हुआ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"किसी भी समय सबसे हाल ही में ऐक्सेस किए गए ऐप्लिकेशन"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"पिछले सात दिनों में सबसे हाल के ऐक्सेस"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"पिछले एक घंटे में अनुमति का इस्तेमाल"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"पिछले 15 मिनटों में अनुमति का इस्तेमाल"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"पिछले एक मिनट में अनुमति का इस्तेमाल"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"पिछले 24 घंटों में इस्तेमाल नहीं किया गया"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"पिछले सात दिनों में इस्तेमाल नहीं किया गया"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{पिछले # दिन में इस्तेमाल नहीं की गई}one{पिछले # दिन में इस्तेमाल नहीं की गई}other{पिछले # दिनों में इस्तेमाल नहीं की गई}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{पिछले # घंटे में इस्तेमाल नहीं की गई}one{पिछले # घंटे में इस्तेमाल नहीं की गई}other{पिछले # घंटों में इस्तेमाल नहीं की गई}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ऐप्लिकेशन ने इस्तेमाल किया}one{# ऐप्लिकेशन ने इस्तेमाल किया}other{# ऐप्लिकेशन ने इस्तेमाल किया}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"डैशबोर्ड में सभी को देखें"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"इससे फ़िल्टर किया गया: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"सिर्फ़ मीडिया फ़ाइलें ऐक्सेस करने की अनुमति दें"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"हमेशा के लिए अनुमति दें"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"सिर्फ़ ऐप्लिकेशन इस्तेमाल करते समय अनुमति दें"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"हर बार पूछें"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति न दें"</string> <string name="precise_image_description" msgid="6349638632303619872">"सटीक जगह"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"इन ऐप्लिकेशन के पास अनुमति नहींं है"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ऐसे और ऐप्लिकेशन देखें जो सभी फ़ाइलों को ऐक्सेस कर सकते हैं"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 दिन}one{# दिन}other{# दिन}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 घंटा}one{# घंटा}other{# घंटे}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 मिनट}one{# मिनट}other{# मिनट}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 सेकंड}one{# सेकंड}other{# सेकंड}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# घंटा}one{# घंटा}other{# घंटे}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# मिनट}one{# मिनट}other{# मिनट}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# सेकंड}one{# सेकंड}other{# सेकंड}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"अनुमति रिमाइंडर"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"एक ऐप्लिकेशन इस्तेमाल नहीं किया जा रहा"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ऐप्लिकेशन इस्तेमाल नहीं किए जा रहे"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को डिवाइस में मौजूद <b>फ़ोटो, वीडियो, संगीत, ऑडियो, और अन्य फ़ाइल</b> का ऐक्सेस देना है?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस में मौजूद संगीत और ऑडियो ऐक्सेस करने की अनुमति देनी है?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस में मौजूद फ़ोटो और वीडियो ऐक्सेस करने की अनुमति देनी है?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को ऑडियो रिकॉर्ड करने की अनुमति देनी है?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ऐप्लिकेशन सिर्फ़ तब ही ऑडियो रिकॉर्ड कर पाएगा, जब आप ऐप्लिकेशन इस्तेमाल कर रहे हों"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"क्या आप <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को ऑडियो रिकॉर्ड करने की अनुमति देना चाहते हैं?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"जब कोई ऐप्लिकेशन आपके कॉपी किए गए टेक्स्ट, इमेज या अन्य कॉन्टेंट को ऐक्सेस करे, तो मैसेज से इसकी सूचना पाएं"</string> <string name="show_password_title" msgid="2877269286984684659">"पासवर्ड दिखाएं"</string> <string name="show_password_summary" msgid="1110166488865981610">"टाइप करते समय वर्ण दिखाएं"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml index 2b2b1dea0..0f1d9a3d0 100644 --- a/PermissionController/res/values-hr/strings.xml +++ b/PermissionController/res/values-hr/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadržite \"Dok se aplikacija koristi\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži \"Samo ovaj put\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više podataka"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dopusti pristup svim fotografijama"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Odaberi fotografije"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Odaberi više fotografija"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne biraj više slika"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ipak nemoj dopustiti"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 h}one{# h}few{# h}other{# h}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}one{# s}few{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dan}one{# dan}few{# dana}other{# dana}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}few{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Bilo koje dopuštenje"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Bilo kad"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Posljednjih tjedan dana"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Posljednja 24 sata"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Posljednjih sat vremena"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Posljednjih 15 minuta"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Posljednja minuta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{posljednji # dan}one{Posljednji # dan}few{Posljednja # dana}other{Posljednjih # dana}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Posljednjih sat vremena}one{Posljednji # sat}few{Posljednja # sata}other{Posljednjih # sati}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Posljednja # minuta}one{Posljednja # minuta}few{Posljednje # minute}other{Posljednjih # minuta}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nema upotreba dopuštenja"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Posljednji pristup u bilo koje vrijeme"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Posljednji pristup u prethodnih tjedan dana"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Upotreba dopuštenja u posljednjih sat vremena"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Upotreba dopuštenja u posljednjih 15 minuta"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Upotreba dopuštenja u posljednjoj minuti"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nije korišteno u posljednja 24 sata"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nije korišteno u posljednjih sedam dana"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nije korišteno u posljednji # dan}one{Nije korišteno u posljednji # dan}few{Nije korišteno u posljednja # dana}other{Nije korišteno u posljednjih # dana}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nije korišteno u posljednji # sat}one{Nije korišteno u posljednji # sat}few{Nije korišteno u posljednja # sata}other{Nije korišteno u posljednjih # sati}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Koristi 1 aplikacija}one{Koristi # aplikacija}few{Koriste # aplikacije}other{Koristi # aplikacija}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Pogledajte sve na nadzornoj ploči"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrirano po: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Omogući pristup samo medijima"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dopusti cijelo vrijeme"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dopusti samo dok se aplikacija koristi"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dopusti sve fotografije"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dopusti odabrane fotografije"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dopustiti"</string> <string name="precise_image_description" msgid="6349638632303619872">"Točna lokacija"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nemaju dopuštenje"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Pogledajte koje još aplikacije imaju pristup svim datotekama"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 h}one{# h}few{# h}other{# h}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 min}one{# min}few{# min}other{# min}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 s}one{# s}few{# s}other{# s}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# s}one{# s}few{# s}other{# s}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Podsjetnici za dopuštenja"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nekorištena aplikacija"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Nekorištenih aplikacija: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dopustiti apl. <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup <b>foto/video/audio i drugim datotekama te glazbi</b> na uređaju?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup glazbi i audiodatotekama na ovom uređaju?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup fotografijama i videozapisima na ovom uređaju?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Želite li aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> omogućiti pristup dodatnim fotografijama?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da snima audiozapise?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati audiozapise samo dok je upotrebljavate"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da snima audiozapise?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Primite poruku kad aplikacije pristupe tekstu, slikama ili drugom kopiranom sadržaju"</string> <string name="show_password_title" msgid="2877269286984684659">"Prikaži zaporke"</string> <string name="show_password_summary" msgid="1110166488865981610">"Nakratko prikaži znakove tijekom unosa"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikacija je navela da može dijeliti <xliff:g id="PERMISSION_NAME">%s</xliff:g> podatke s trećim stranama"</string> </resources> diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml index 9b45a8143..afd1002b8 100644 --- a/PermissionController/res/values-hu/strings.xml +++ b/PermissionController/res/values-hu/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Továbbra is: „Amíg az alkalmazás használatban van”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Maradjon „Csak most”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Bővebben"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Hozzáférés engedélyezése az összes fotóhoz"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotók kijelölése"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"További fotók kijelölése"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne jelöljön ki több fotót"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Semmiképpen se engedélyezze"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Elvetés"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>/<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>."</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 nap}other{# nap}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 óra}other{# óra}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 perc}other{# perc}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 mp}other{# mp}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# nap}other{# nap}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# óra}other{# óra}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# perc}other{# perc}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# mp}other{# mp}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Bármely engedély"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Bármikor"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Elmúlt 7 nap"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Elmúlt 24 óra"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Elmúlt 1 óra"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Elmúlt 15 perc"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Elmúlt 1 perc"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Elmúlt # nap}other{Elmúlt # nap}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Elmúlt # óra}other{Elmúlt # óra}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Elmúlt # perc}other{Elmúlt # perc}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nincs engedélyhasználat"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Legutóbbi hozzáférés bármikor"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Legutóbbi hozzáférés az elmúlt hét nap során"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Engedélyhasználat az elmúlt egy órában"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Engedélyhasználat az elmúlt 15 percben"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Engedélyhasználat az elmúlt egy percben"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Semmi nem használta az elmúlt 24 órában"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Semmi nem használta az elmúlt hét napban"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nem volt használva az elmúlt # nap során}other{Semmi nem használta az elmúlt # napban}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nem volt használva az elmúlt # óra során}other{Semmi nem használta az elmúlt # órában}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 alkalmazás használta}other{# alkalmazás használta}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Összes megtekintése az irányítópulton"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Szűrés alapja: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Csak a médiatartalmakhoz való hozzáférés engedélyezése"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Mindig engedélyezett"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Csak az alkalmazás használatakor engedélyezett"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Összes fotó engedélyezése"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Kijelölt fotók engedélyezése"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Mindig kérdezzen rá"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Tiltás"</string> <string name="precise_image_description" msgid="6349638632303619872">"Pontos hely"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nem engedélyezett"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"További, minden fájlhoz hozzáférő alkalmazások megtekintése"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 nap}other{# nap}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 óra}other{# óra}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 perc}other{# perc}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 másodperc}other{# másodperc}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# óra}other{# óra}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# perc}other{# perc}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# másodperc}other{# másodperc}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Engedélyekre vonatkozó emlékeztetők"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nem használt alkalmazás"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> nem használt alkalmazás"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Hozzáférhet a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> az eszközön tárolt <b>fotókhoz, hang-, videó- és egyéb fájlokhoz</b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Hozzáférhet a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> az eszközön tárolt zenékhez és egyéb hanganyagokhoz?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Hozzáférhet a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> az eszközön tárolt fotókhoz és videókhoz?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Engedélyezi, hogy a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> további fotókhoz férjen hozzá?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hangfelvételt készíthessen?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Az alkalmazás csak akkor tud majd hangfelvételt készíteni, amikor Ön használja az alkalmazást."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hangfelvételt készíthessen?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Üzenet megjelenítése, amikor alkalmazások férnek hozzá a vágólapra másolt szövegekhez, képekhez vagy más tartalmakhoz"</string> <string name="show_password_title" msgid="2877269286984684659">"Jelszavak mutatása"</string> <string name="show_password_summary" msgid="1110166488865981610">"Gépelés közben rövid ideig megjeleníti a karaktereket"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Az alkalmazás jelezte, hogy megoszthat a(z) <xliff:g id="PERMISSION_NAME">%s</xliff:g> jogosultsággal kapcsolatos adatokat harmadik felekkel"</string> </resources> diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml index 0c57d6f61..60af5358d 100644 --- a/PermissionController/res/values-hy/strings.xml +++ b/PermissionController/res/values-hy/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Թույլատրել, միայն երբ հավելվածն ակտիվ է"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Պահպանել «Միայն այս անգամ» կարգավորումը"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Մանրամասն"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Հասանելի դարձնել բոլոր լուսանկարները"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Ընտրել լուսանկարներ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Ընտրել այլ լուսանկարներ"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Չընտրել այլ լուսանկարներ"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Միևնույն է չթույլատրել"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Փակել"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 օր}one{# օր}other{# օր}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ժամ}one{# ժամ}other{# ժամ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 րոպե}one{# րոպե}other{# րոպե}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 վրկ}one{# վրկ}other{# վրկ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# օր}one{# օր}other{# օր}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ժամ}one{# ժամ}other{# ժամ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# րոպե}one{# րոպե}other{# րոպե}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# վրկ}one{# վրկ}other{# վրկ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Բոլոր թույլտվությունները"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Ցանկացած ժամանակ"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Վերջին 7 օրում"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Վերջին 24 ժամում"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Վերջին ժամում"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Վերջին 15 րոպեում"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Վերջին 1 րոպեում"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Վերջին # օրում}one{Վերջին # օրում}other{Վերջին # օրում}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Վերջին # ժամում}one{Վերջին # ժամում}other{Վերջին # ժամում}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Վերջին # րոպեում}one{Վերջին # րոպեում}other{Վերջին # րոպեում}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Թույլտվություններ չեն կիրառվել"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Վերջին օգտագործումը ցանկացած ժամանակ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Վերջին օգտագործումը վերջին 7 օրում"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Թույլտվությունների օգտագործումը վերջին 1 ժամում"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Թույլտվությունների օգտագործումը վերջին 15 րոպեում"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Թույլտվությունների օգտագործումը վերջին 1 րոպեում"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Չի օգտագործվել վերջին 24 ժամվա ընթացքում"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Չի օգտագործվել վերջին 7 օրվա ընթացքում"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Չի օգտագործվել վերջին # օրվա ընթացքում}one{Չի օգտագործվել վերջին # օրվա ընթացքում}other{Չի օգտագործվել վերջին # օրվա ընթացքում}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Չի օգտագործվել վերջին # ժամվա ընթացքում}one{Չի օգտագործվել վերջին # ժամվա ընթացքում}other{Չի օգտագործվել վերջին # ժամվա ընթացքում}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Օգտագործվում է 1 հավելվածի կողմից}one{Օգտագործվում է # հավելվածի կողմից}other{Օգտագործվում է # հավելվածի կողմից}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Դիտել բոլորը կառավարման վահանակում"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Զտիչ՝ <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Հասանելի դարձնել միայն մեդիաֆայլերը"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Միշտ թույլատրել"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Թույլատրել միայն հավելվածի օգտագործման ժամանակ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Թույլատրել բոլոր լուսանկարները"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Թույլատրել ընտրված լուսանկարները"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ամեն անգամ հարցնել"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Չթույլատրել"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ճշգրիտ տեղադրություն"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Արգելված"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Տեսնել այլ հավելվածներ, որոնց հասանելի են բոլոր ֆայլերը"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 օր}one{# օր}other{# օր}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ժամ}one{# ժամ}other{# ժամ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 րոպե}one{# րոպե}other{# րոպե}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 վայրկյան}one{# վայրկյան}other{# վայրկյան}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ժամ}one{# ժամ}other{# ժամ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# րոպե}one{# րոպե}other{# րոպե}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# վայրկյան}one{# վայրկյան}other{# վայրկյան}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Հիշեցումներ թույլտվությունների մասին"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 չօգտագործվող հավելված"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> չօգտագործվող հավելված"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել այս սարքի <b>նկարները, երգերը, տեսանյութերը, աուդիո ֆայլերը և մյուս ֆայլերը</b>"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել այս սարքի երաժշտությունը և մյուս աուդիո ֆայլերը"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Հասանելի դարձնե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին այլ լուսանկարներ։"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Թույլատրե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին ձայնագրել"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Այս հավելվածը կկարողանա ձայնագրություններ անել միայն, երբ այն օգտագործելիս լինեք"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Թույլատրե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին ձայնագրություններ անել։"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Ցուցադրել հաղորդագրություն, երբ հավելվածներին հասանելի են դառնում ձեր պատճենած տեքստը, պատկերները կամ այլ բովանդակություն"</string> <string name="show_password_title" msgid="2877269286984684659">"Ցուցադրել գաղտնաբառերը"</string> <string name="show_password_summary" msgid="1110166488865981610">"Տեքստ մուտքագրելիս կարճ ժամանակով ցուցադրել գրանշանները"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Այս հավելվածը կարող է երրորդ կողմերի հետ կիսվել «<xliff:g id="PERMISSION_NAME">%s</xliff:g>» կատեգորիային առնչվող տվյալներով"</string> </resources> diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml index 11cd4dfe7..ab0929d8d 100644 --- a/PermissionController/res/values-in/strings.xml +++ b/PermissionController/res/values-in/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Pertahankan \"Saat aplikasi digunakan\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Pertahankan \"Hanya kali ini\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Info lengkap"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Izinkan akses ke semua foto"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pilih foto"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pilih foto lainnya"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Jangan pilih foto lagi"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Bagaimanapun jangan izinkan"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Tutup"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> dari <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 hari}other{# hari}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 jam}other{# jam}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 mnt}other{# mnt}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 dtk}other{# dtk}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# hari}other{# hari}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# jam}other{# jam}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# menit}other{# menit}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# detik}other{# detik}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Izin apa pun"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Kapan saja"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 hari terakhir"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 jam terakhir"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 jam terakhir"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 menit terakhir"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 menit terakhir"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# hari terakhir}other{# hari terakhir}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# jam terakhir}other{# jam terakhir}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# menit terakhir}other{# menit terakhir}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Tidak ada penggunaan izin"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Akses terbaru kapan saja"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Akses terbaru dalam 7 hari terakhir"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Penggunaan izin dalam 1 jam terakhir"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Penggunaan izin dalam 15 menit terakhir"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Penggunaan izin dalam 1 menit terakhir"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Tidak digunakan dalam 24 jam terakhir"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Tidak digunakan dalam 7 hari terakhir"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Tidak digunakan dalam # hari terakhir}other{Tidak digunakan dalam # hari terakhir}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Tidak digunakan dalam # jam terakhir}other{Tidak digunakan dalam # jam terakhir}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Digunakan 1 aplikasi}other{Digunakan # aplikasi}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Lihat semua di Dasbor"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Difilter menurut: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Izinkan akses hanya ke media"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Izinkan sepanjang waktu"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Izinkan hanya saat aplikasi digunakan"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Izinkan semua foto"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Izinkan foto yang dipilih"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Selalu tanya"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan izinkan"</string> <string name="precise_image_description" msgid="6349638632303619872">"Lokasi akurat"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Tidak diizinkan"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Lihat aplikasi lain yang dapat mengakses semua file"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 hari}other{# hari}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 jam}other{# jam}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 menit}other{# menit}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 detik}other{# detik}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# jam}other{# jam}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# menit}other{# menit}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# detik}other{# detik}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Pengingat izin"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplikasi yang tidak digunakan"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplikasi tidak digunakan"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses <b>foto, video, musik, audio, dan file lainnya</b> di perangkat ini?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses musik dan audio di perangkat ini?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses foto dan video di perangkat ini?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Beri <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> akses ke foto lainnya?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> merekam audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikasi hanya dapat merekam audio saat aplikasi sedang digunakan"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> merekam audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Menampilkan pesan saat aplikasi mengakses teks, gambar, atau konten lainnya yang telah Anda salin"</string> <string name="show_password_title" msgid="2877269286984684659">"Tampilkan sandi"</string> <string name="show_password_summary" msgid="1110166488865981610">"Menampilkan karakter sejenak saat Anda mengetik"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikasi ini menyatakan bahwa aplikasi mungkin membagikan data <xliff:g id="PERMISSION_NAME">%s</xliff:g> ke pihak ketiga"</string> </resources> diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml index ea631a8ce..1db9c544a 100644 --- a/PermissionController/res/values-is/strings.xml +++ b/PermissionController/res/values-is/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Halda „Þegar forritið er í notkun“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Halda „Aðeins í þetta skipti“"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Upplýsingar"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Leyfa aðgang að öllum myndum"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Velja myndir"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Velja fleiri myndir"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ekki velja fleiri myndir"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ekki leyfa þrátt fyrir það"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Loka"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dagur}one{# dagur}other{# dagar}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 klukkustund}one{# klukkustund}other{# klukkustundir}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 mín.}one{# mín.}other{# mín.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek.}one{# sek.}other{# sek.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dagur}one{# dagur}other{# dagar}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# klukkustund}one{# klukkustund}other{# klukkustundir}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# mín.}one{# mín.}other{# mín.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek.}one{# sek.}other{# sek.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Hvaða heimild sem er"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Hvenær sem er"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Síðustu sjö daga"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Síðasti sólarhringur"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Síðustu klukkustund"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Síðustu 15 mínútur"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Síðasta mínúta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Undanfarinn # dag}one{Undanfarinn # dag}other{Undanfarna # daga}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Síðustu # klst.}one{Síðustu # klst.}other{Síðustu # klst.}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Síðasta # mín.}one{Síðustu # mín.}other{Síðustu # mín.}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Engin heimildanotkun"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Síðasti aðgangur, hvenær sem er"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Síðasti aðgangur síðustu 7 daga"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Heimildanotkun síðustu klukkustund"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Heimildanotkun síðustu 15 mínútur"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Heimildanotkun síðustu mínútu"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ekki notað síðasta sólarhringinn"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Ekki notað síðastliðna 7 daga"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Ekki notað undanfarinn # dag}one{Ekki notað undanfarinn # dag}other{Ekki notað undanfarna # daga}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Ekki notað síðustu # klst.}one{Ekki notað síðustu # klst.}other{Ekki notað síðustu # klst.}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Notað af 1 forriti}one{Notað af # forriti}other{Notað af # forritum}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Sjá allt á stjórnborði"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Síað eftir: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Leyfa aðeins aðgang að margmiðlunarefni"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Leyfa alltaf"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Leyfa aðeins þegar forritið er í notkun"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Leyfa allar myndir"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Leyfa valdar myndir"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Spyrja alltaf"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ekki leyfa"</string> <string name="precise_image_description" msgid="6349638632303619872">"Nákvæm staðsetning"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ekki heimilað"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Sjá fleiri forrit sem geta opnað allar skrár"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dagur}one{# dagur}other{# dagar}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 klukkustund}one{# klukkustund}other{# klukkustundir}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 mínúta}one{# mínúta}other{# mínútur}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekúnda}one{# sekúnda}other{# sekúndur}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# klukkustund}one{# klukkustund}other{# klukkustundir}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# mínúta}one{# mínúta}other{# mínútur}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekúnda}one{# sekúnda}other{# sekúndur}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Áminningar um heimild"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ónotað forrit"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ónotuð forrit"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að <b>myndum, myndskeiðum, tónlist, hljóði og öðrum skrám</b> í þessu tæki?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að tónlist og hljóði í þessu tæki?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að myndum og myndskeiðum í þessu tæki?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að fleiri myndum?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leyfa <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> að taka upp hljóð?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Forritið mun aðeins geta tekið upp hljóð þegar þú ert að nota forritið"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Viltu leyfa <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> að taka upp hljóð?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Birta skilaboð þegar forrit fá aðgang að texta, myndum eða öðru efni sem þú hefur afritað"</string> <string name="show_password_title" msgid="2877269286984684659">"Sýna aðgangsorð"</string> <string name="show_password_summary" msgid="1110166488865981610">"Birta stafi í stutta stund þegar þú skrifar"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Þetta forrit gaf til kynna að það kunni að deila gögnum af gerðinni „<xliff:g id="PERMISSION_NAME">%s</xliff:g>“ með þriðju aðilum"</string> </resources> diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml index 7199e0045..3c6170449 100644 --- a/PermissionController/res/values-it/strings.xml +++ b/PermissionController/res/values-it/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantieni \"Mentre l\'app è in uso\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantieni solo questa volta"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Altre info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Consenti l\'accesso a tutte le foto"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleziona foto"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleziona altre foto"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Non selezionare altre foto"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Non consentire comunque"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignora"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> di <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 giorno}many{# giorni}other{# giorni}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ora}many{# ore}other{# ore}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}many{# sec}other{# sec}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# giorno}many{# giorni}other{# giorni}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sec}many{# sec}other{# sec}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualsiasi autorizzazione"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Qualsiasi data"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Ultimi 7 giorni"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Ultime 24 ore"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Ultima ora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Ultimi 15 minuti"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Ultimo minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultimo giorno}many{Ultimi # giorni}other{Ultimi # giorni}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ultima ora}many{Ultime # ore}other{Ultime # ore}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ultimo minuto}many{Ultimi # minuti}other{Ultimi # minuti}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Autorizzazioni non usate"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Accesso più recente in qualsiasi momento"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Accesso più recente negli ultimi 7 giorni"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso autorizzazioni nell\'ultima ora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso autorizzazioni negli ultimi 15 minuti"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso autorizzazioni nell\'ultimo minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Autorizzazione non utilizzata nelle ultime 24 ore"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Autorizzazione non utilizzata negli ultimi 7 giorni"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultimo giorno}many{Autorizzazione non utilizzata negli ultimi # giorni}other{Autorizzazione non utilizzata negli ultimi # giorni}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultima ora}many{Autorizzazione non utilizzata nelle ultime # ore}other{Autorizzazione non utilizzata nelle ultime # ore}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Autorizzazione usata da 1 app}many{Used by # apps}other{Autorizzazione usata da # app}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Mostra tutto nella Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrata per: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Consenti l\'accesso solo ai file multimediali"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Consenti sempre"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Consenti solo mentre l\'app è in uso"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Consenti tutte le foto"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Consenti le foto selezionate"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Chiedi ogni volta"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Non consentire"</string> <string name="precise_image_description" msgid="6349638632303619872">"Posizione esatta"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Non autorizzate"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Visualizza altre app che possono accedere a tutti i file"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 giorno}many{# giorni}other{# giorni}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ora}many{# ore}other{# ore}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}many{# minuti}other{# minuti}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 secondo}many{# secondi}other{# secondi}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minuti}other{# minuti}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# secondo}many{# secondi}other{# secondi}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Promemoria autorizzazione"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app inutilizzata"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> app inutilizzate"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere a <b>foto, video, musica, audio e altri file</b> sul dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere a musica e audio sul dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere a foto e video sul dispositivo?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vuoi consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere ad altre foto?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di registrare audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'app potrà registrare audio soltanto quando la usi"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vuoi consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di registrare audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Viene mostrato un messaggio quando le app accedono a testo, immagini o altri contenuti che hai copiato"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostra password"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostra brevemente i caratteri durante la digitazione"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Questa app ha dichiarato che potrebbe condividere dati <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terze parti"</string> </resources> diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml index 4525e9c1b..dd8a144d0 100644 --- a/PermissionController/res/values-iw/strings.xml +++ b/PermissionController/res/values-iw/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"אני רוצה להשאיר את האפשרות \"כשהאפליקציה נמצאת בשימוש\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"אני רוצה לשמור על ההגדרה “רק הפעם”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"מידע נוסף"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"אישור גישה לכל התמונות"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"בחירת תמונות"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"בחירת תמונות נוספות"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"לא לבחור תמונות נוספות"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"אין אישור בכל זאת"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"סגירה"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> מתוך <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{יום אחד}two{יומיים}many{# ימים}other{# ימים}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{שעה אחת}two{שעתיים}many{# שעות}other{# שעות}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{דקה אחת}two{# דקות}many{# דקות}other{# דקות}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{שנייה אחת}two{# שניות}many{# שניות}other{# שניות}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{יום אחד}two{יומיים}many{# ימים}other{# ימים}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{שעה}two{שעתיים}many{# שעות}other{# שעות}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{דקה אחת}two{# דקות}many{# דקות}other{# דקות}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{שנייה אחת}two{# שניות}many{# שניות}other{# שניות}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"כל הרשאה שהיא"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"בכל עת"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 הימים האחרונים"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ב-24 השעות החולפות"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"בשעה האחרונה"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 הדקות האחרונות"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"הדקה האחרונה"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ביום האחרון}two{ביומיים האחרונים}many{ב-# הימים האחרונים}other{ב-# הימים האחרונים}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{בשעה האחרונה}two{בשעתיים האחרונות}many{ב-# השעות האחרונות}other{ב-# השעות האחרונות}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{בדקה האחרונה}two{ב-# הדקות האחרונות}many{ב-# הדקות האחרונות}other{ב-# הדקות האחרונות}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"אין שימוש בהרשאות"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"הגישה האחרונה בכל מסגרת זמן"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"הגישה האחרונה ב-7 הימים האחרונים"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"שימוש בהרשאות בשעה האחרונה"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"שימוש בהרשאות ב-15 הדקות האחרונות"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"שימוש בהרשאות בדקה האחרונה"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"לא נעשה שימוש ב-24 השעות האחרונות"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"לא נעשה שימוש ב-7 הימים האחרונים"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{לא נעשה שימוש ביום האחרון}two{לא נעשה שימוש ביומיים האחרונים}many{לא נעשה שימוש ב-# הימים האחרונים}other{לא נעשה שימוש ב-# הימים האחרונים}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{לא נעשה שימוש בשעה האחרונה}two{לא נעשה שימוש בשעתיים האחרונות}many{לא נעשה שימוש ב-# השעות האחרונות}other{לא נעשה שימוש ב-# השעות האחרונות}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{בשימוש על ידי אפליקציה אחת}two{בשימוש על ידי # אפליקציות}many{בשימוש על ידי # אפליקציות}other{בשימוש על ידי # אפליקציות}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"הצגת כל הפרטים במרכז השליטה"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"סינון לפי: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"אישור גישה למדיה בלבד"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"כן, כל הזמן"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"רק כשהאפליקציה בשימוש"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"גישה לכל התמונות"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"גישה לתמונות נבחרות"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"יש לשאול בכל פעם"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"אין אישור"</string> <string name="precise_image_description" msgid="6349638632303619872">"מיקום מדויק"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"אין הרשאה"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"לצפייה באפליקציות נוספות שיכולות לגשת לכל הקבצים"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{יום אחד}two{יומיים}many{# ימים}other{# ימים}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{שעה אחת}two{שעתיים}many{# שעות}other{# שעות}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{דקה אחת}two{# דקות}many{# דקות}other{# דקות}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{שנייה אחת}two{# שניות}many{# שניות}other{# שניות}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{שעה}two{שעתיים}many{# שעות}other{# שעות}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{דקה אחת}two{# דקות}many{# דקות}other{# דקות}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{שנייה אחת}two{# שניות}many{# שניות}other{# שניות}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"תזכורות להרשאות"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"אפליקציה אחת שמזמן לא השתמשת בה"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> אפליקציות שמזמן לא השתמשת בהן"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה <b>לתמונות, לסרטונים, למוזיקה, לאודיו ולקבצים אחרים</b> במכשיר?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למוזיקה ולקובצי אודיו במכשיר?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה לתמונות ולסרטונים במכשיר?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> גישה לתמונות נוספות?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"לאשר לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> להקליט אודיו?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"האפליקציה תוכל להקליט אודיו רק כאשר היא בשימוש"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאה להקליט אודיו?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"הצגת הודעה בזמן גישה של אפליקציות לטקסט, לתמונות או לכל תוכן אחר שהעתקת"</string> <string name="show_password_title" msgid="2877269286984684659">"הצגת סיסמאות"</string> <string name="show_password_summary" msgid="1110166488865981610">"התווים יופיעו לפרקי זמן קצרים בזמן ההקלדה"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"האפליקציה הזו הצהירה שהיא עשויה לשתף נתונים של <xliff:g id="PERMISSION_NAME">%s</xliff:g> עם צדדים שלישיים"</string> </resources> diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml index 200e704fd..7d980084c 100644 --- a/PermissionController/res/values-ja/strings.xml +++ b/PermissionController/res/values-ja/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"[アプリが使用中の場合] を保持"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"「今回のみ」の設定を維持"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"詳細"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"すべての写真へのアクセスを許可"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"写真を選択"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"他の写真を選択"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"他の写真を選択しない"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"許可しない"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"閉じる"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 日}other{# 日}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 時間}other{# 時間}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 分}other{# 分}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# 日}other{# 日}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# 時間}other{# 時間}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# 分}other{# 分}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"すべての権限"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"全期間"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"過去 7 日間"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"過去 24 時間"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"過去 1 時間"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"過去 15 分間"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"過去 1 分間"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{過去 # 日}other{過去 # 日間}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{過去 # 時間}other{過去 # 時間}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{過去 # 分間}other{過去 # 分間}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"権限の使用はなし"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"最近のアクセス状況(常時)"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"最近のアクセス状況(過去 7 日間)"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"権限の使用状況(過去 1 時間)"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"権限の使用状況(過去 15 分間)"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"権限の使用状況(過去 1 分間)"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"過去 24 時間では使用されていません"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"過去 7 日間では使用されていません"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{過去 # 日は使用されていません}other{過去 # 日間は使用されていません}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{過去 # 時間は使用されていません}other{過去 # 時間は使用されていません}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 個のアプリで使用}other{# 個のアプリで使用}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ダッシュボードにすべて表示"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"フィルタ: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"メディアへのアクセスのみを許可"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"常に許可"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"アプリの使用中のみ許可"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"すべての写真を許可"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"一部の写真を許可"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"毎回確認する"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"許可しない"</string> <string name="precise_image_description" msgid="6349638632303619872">"正確な現在地"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"許可しない"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"すべてのファイルにアクセスできるアプリをもっと見る"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 日}other{# 日}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 時間}other{# 時間}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 分}other{# 分}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 時間}other{# 時間}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# 分}other{# 分}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"権限のリマインダー"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"使用されていないアプリ: 1 個"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"使用されていないアプリ: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> 個"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"このデバイス内の<b>写真、動画、音楽、音声など</b>へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"このデバイス内の音楽と音声へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"このデバイス内の写真と動画へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に他の写真へのアクセスを許可しますか?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"音声の録音を「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"アプリは、ユーザーがアプリを使用している場合のみ音声を録音できます"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"音声の録音を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"クリップボードにコピーしたテキストや画像などにアプリがアクセスすると、メッセージで通知する"</string> <string name="show_password_title" msgid="2877269286984684659">"パスワードの表示"</string> <string name="show_password_summary" msgid="1110166488865981610">"入力した文字を短い間表示する"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"このアプリは、<xliff:g id="PERMISSION_NAME">%s</xliff:g>データをサードパーティと共有する可能性があります"</string> </resources> diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml index d8cce80cb..d3959c0e6 100644 --- a/PermissionController/res/values-ka/strings.xml +++ b/PermissionController/res/values-ka/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"დარჩეს „აპის გამოყენებისას“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"დაშვება „მხოლოდ ამ ერთხელ“"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"მეტი ინფორმაცია"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ყველა ფოტოზე წვდომის დაშვება"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ფოტოების არჩევა"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"მეტი ფოტოს არჩევა"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"არ აირჩიო მეტი ფოტო"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"მაინც არ დაიშვას"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"დახურვა"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-დან"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 დღე}other{# დღე}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 საათი}other{# საათი}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 წთ}other{# წთ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 წმ}other{# წმ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# დღე}other{# დღე}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# საათი}other{# საათი}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# წთ}other{# წთ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# წმ}other{# წმ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ნებისმიერი ნებართვა"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ნებისმიერი დრო"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ბოლო 7 დღე"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ბოლო 24 საათი"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"ბოლო 1 საათი"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ბოლო 15 წუთი"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ბოლო 1 წუთი"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ბოლო # დღე}other{ბოლო # დღე}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{ბოლო # საათი}other{ბოლო # საათი}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ბოლო # წუთი}other{ბოლო # წუთი}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ამ ნებართვებს აპები არ იყენებს"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"უახლესი წვდომა ნებისმიერი დროისთვის"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"უახლესი წვდომა ბოლო 7 დღეში"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ნებართვების გამოყენება ბოლო 1 საათში"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ნებართვების გამოყენება ბოლო 15 წუთში"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ნებართვების გამოყენება ბოლო 1 წუთში"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"არ გამოყენებულა ბოლო 24 საათის განმავლობაში"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"არ გამოყენებულა ბოლო 7 დღის განმავლობაში"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{არ გამოყენებულა ბოლო # დღეში}other{არ გამოყენებულა ბოლო # დღეში}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{არ გამოყენებულა ბოლო # საათში}other{არ გამოყენებულა ბოლო # საათში}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{იყენებს 1 აპი}other{იყენებს # აპი}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ყველაფრის ნახვა საინფორმაციო დაფაზე"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"გაფილტვრის კრიტერიუმი: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"მხოლოდ მედიაზე წვდომის დაშვება"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ყოველთვის დაშვება"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"მხოლოდ აპის გამოყენებისას დაშვება"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ყველა ფოტოს დაშვება"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"არჩეული ფოტოების დაშვება"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ყოველთვის მკითხეთ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"არ დაიშვას"</string> <string name="precise_image_description" msgid="6349638632303619872">"ზუსტი მდებარეობა"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"არ არის დაშვებული"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"სხვა აპების ნახვა, რომლებსაც ყველა ფაილზე წვდომა შეუძლია"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 დღე}other{# დღე}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 საათი}other{# საათი}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 წუთი}other{# წუთი}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 წამი}other{# წამი}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# საათი}other{# საათი}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# წუთი}other{# წუთი}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# წამი}other{# წამი}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"შეხსენებები ნებართვის შესახებ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 გამოუყენებელი აპი"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> გამოუყენებელი აპი"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"მიანიჭებთ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-ის წვდომას თქვენი მოწყ. <b>ფოტოებზე, ვიდეოებზე, მუსიკაზე, აუდიო და სხვა </b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"მიანიჭებთ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-ს წვდომას თქვენი მოწყობილობის მუსიკასა და აუდიოფაილებზე?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"მიანიჭებთ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-ის წვდომას თქვენი მოწყობილობის ფოტოებსა და ვიდეოებზე?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"მეტ ფოტოზე წვდომის ნებას დართავთ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> აპს?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"გსურთ, მიანიჭოთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</b> აუდიოს ჩაწერის ნებართვა?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ეს აპი აუდიოს ჩაწერას მხოლოდ მაშინ შეძლებს, როცა მას იყენებთ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"გსურთ, მიანიჭოთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</b> აუდიოს ჩაწერის ნებართვა?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"როდესაც აპებს თქვენ მიერ კოპირებულ ტექსტზე, სურათებზე ან სხვა კონტენტზე აქვთ წვდომა, გამოჩნდება შეტყობინება"</string> <string name="show_password_title" msgid="2877269286984684659">"პაროლების ჩვენება"</string> <string name="show_password_summary" msgid="1110166488865981610">"აკრეფისას სიმბოლოების ხანმოკლედ გამოჩენა"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ამ აპის თანახმად, მან შესაძლოა გაუზიაროს <xliff:g id="PERMISSION_NAME">%s</xliff:g>-ის მონაცემები მესამე მხარეს"</string> </resources> diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml index 38974f19a..65ab340ed 100644 --- a/PermissionController/res/values-kk/strings.xml +++ b/PermissionController/res/values-kk/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Қолданба пайдаланылатын кезде\" түймесін басып тұрыңыз"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Тек осы жолы\" күйінде қалдыру"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Толығырақ"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Барлық фотосуретті пайдалануға рұқсат беру"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Фотосурет таңдау"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Көбірек фотосурет таңдау"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Басқа фотосурет таңдамау"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Бәрібір рұқсат бермеу"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Жабу"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 күн}other{# күн}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 сағат}other{# сағат}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мин}other{# мин}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 с}other{# с}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# күн}other{# күн}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# сағат}other{# сағат}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мин}other{# мин}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# с}other{# с}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Кез келген рұқсат"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Кез келген уақытта"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Соңғы 7 күн"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Соңғы 24 сағат"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Соңғы 1 сағат"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Соңғы 15 минут"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Соңғы 1 минут"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Соңғы # күн}other{Соңғы # күн}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Соңғы # сағат}other{Соңғы # сағат}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Соңғы # минут}other{Соңғы # минут}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Рұқсаттар пайдаланылмаған"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Бүкіл уақыт бойғы соңғы пайдаланылуы"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Соңғы 7 күндегі пайдаланылуы"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Рұқсаттың соңғы 1 сағатта пайдаланылуы"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Рұқсаттың соңғы 15 минутта пайдаланылуы"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Рұқсаттың соңғы 1 минутта пайдаланылуы"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Соңғы 24 сағатта пайдаланылмады."</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Соңғы 7 күнде пайдаланылмады."</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Соңғы # күнде пайдаланылмады.}other{Соңғы # күнде пайдаланылмады.}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Соңғы # сағатта пайдаланылмады.}other{Соңғы # сағатта пайдаланылмады.}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 қолданба пайдаланды}other{# қолданба пайдаланды}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Барлығын бақылау тақтасынан көру"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Сүзгі шарты: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Тек медиафайлдарды пайдалануға рұқсат беру"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Біржола рұқсат беру"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Қолданбаны пайдаланғанда ғана рұқсат беру"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Барлық фотосуретке рұқсат беру"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Таңдалған фотосуреттерге рұқсат беру"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Әрдайым сұрау"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Рұқсат бермеу"</string> <string name="precise_image_description" msgid="6349638632303619872">"Нақты орын"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Рұқсат берілмегендер"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Барлық файлды пайдалана алатын тағы басқа қолданбаларды көру"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 күн}other{# күн}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 сағат}other{# сағат}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минут}other{# минут}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунд}other{# секунд}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# сағат}other{# сағат}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минут}other{# минут}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунд}other{# секунд}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Рұқсат туралы еске салғыштар"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 пайдаланылмайтын қолданба"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> пайдаланылмайтын қолданба бар"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына осы құрылғыдағы <b>фотосурет, бейне, музыка, аудио мен басқа файлдарды</b> пайдалану рұқсаты берілсін бе?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына осы құрылғыдағы музыка мен аудионы пайдалану рұқсаты берілсін бе?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына осы құрылғыдағы фотосурет пен бейнені пайдалану рұқсаты берілсін бе?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына көбірек фотосурет пайдалануға рұқсат беру керек пе?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына дыбыс жазуға рұқсат берілсін бе?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Қолданба тек жұмыс кезінде ғана аудиомазмұн жаза алады."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына аудиомазмұн жазуға рұқсат берілсін бе?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Қолданбалар көшірілген мәтінді, суреттерді немесе басқа мазмұнды пайдаланған кезде хабар көрсету"</string> <string name="show_password_title" msgid="2877269286984684659">"Құпия сөздерді көрсету"</string> <string name="show_password_summary" msgid="1110166488865981610">"Таңбалар терілген кезде аз уақыт көрсетіледі."</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Бұл қолданба <xliff:g id="PERMISSION_NAME">%s</xliff:g> деректерін үшінші тараптармен бөлісуі мүмкін екенін мәлімдеді."</string> </resources> diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml index 1396915f9..45aad0f1d 100644 --- a/PermissionController/res/values-km/strings.xml +++ b/PermissionController/res/values-km/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"រក្សាទុក “ខណៈពេលកំពុងប្រើកម្មវិធី”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"រក្សាទុក “តែពេលនេះប៉ុណ្ណោះ”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ព័ត៌មានបន្ថែម"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"អនុញ្ញាតឱ្យចូលប្រើរូបថតទាំងអស់"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ជ្រើសរើសរូបថត"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ជ្រើសរើសរូបថតច្រើនទៀត"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"កុំជ្រើសរើសរូបថតបន្ថែមទៀត"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"មិនអីទេ មិនអនុញ្ញាត"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ច្រានចោល"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ក្នុងចំណោម <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ថ្ងៃ}other{# ថ្ងៃ}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ម៉ោង}other{# ម៉ោង}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ន}other{# ន}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 វិ}other{# វិ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ថ្ងៃ}other{# ថ្ងៃ}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ម៉ោង}other{# ម៉ោង}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# នាទីមុន}other{# ន}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# វិ}other{# វិ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ការអនុញ្ញាតណាមួយ"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ពេលណាក៏បាន"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 ថ្ងៃចុងក្រោយ"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 ម៉ោងចុងក្រោយ"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 ម៉ោងចុងក្រោយ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 នាទីចុងក្រោយ"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 នាទីចុងក្រោយ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# ថ្ងៃចុងក្រោយ}other{# ថ្ងៃចុងក្រោយ}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ម៉ោងចុងក្រោយ}other{# ម៉ោងចុងក្រោយ}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# នាទីចុងក្រោយ}other{# នាទីចុងក្រោយ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"គ្មានការប្រើប្រាស់ការអនុញ្ញាតទេ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ការចូលប្រើថ្មីបំផុតគ្រប់ពេល"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ការចូលប្រើចុងក្រោយបំផុតក្នុងរយៈពេល 7 ថ្ងៃចុងក្រោយ"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ការប្រើប្រាស់ការអនុញ្ញាតក្នុងរយៈពេល 1 ម៉ោងចុងក្រោយ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ការប្រើប្រាស់ការអនុញ្ញាតក្នុងរយៈពេល 15 នាទីចុងក្រោយ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ការប្រើប្រាស់ការអនុញ្ញាតក្នុងរយៈពេល 1 នាទីចុងក្រោយ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"មិនបានប្រើក្នុងរយៈពេល 24 ម៉ោងចុងក្រោយ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"មិនបានប្រើក្នុងរយៈពេល 7 ថ្ងៃចុងក្រោយ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{មិនបានប្រើក្នុងរយៈពេល # ថ្ងៃចុងក្រោយ}other{មិនបានប្រើក្នុងរយៈពេល # ថ្ងៃចុងក្រោយ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{មិនបានប្រើក្នុងរយៈពេល # ម៉ោងចុងក្រោយ}other{មិនបានប្រើក្នុងរយៈពេល # ម៉ោងចុងក្រោយ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{បានប្រើដោយកម្មវិធី 1}other{បានប្រើដោយកម្មវិធី #}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"មើលទាំងអស់នៅក្នុងផ្ទាំងគ្រប់គ្រង"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ត្រងតាម៖ <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"អនុញ្ញាតឱ្យចូលប្រើមេឌៀតែប៉ុណ្ណោះ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"អនុញ្ញាតគ្រប់ពេល"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"អនុញ្ញាតពេលកំពុងប្រើប្រាស់កម្មវិធីតែប៉ុណ្ណោះ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"អនុញ្ញាតរូបថតទាំងអស់"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"អនុញ្ញាតរូបថតដែលបានជ្រើសរើស"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"សួរគ្រប់ពេល"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"មិនអនុញ្ញាត"</string> <string name="precise_image_description" msgid="6349638632303619872">"ទីតាំងជាក់លាក់"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"មិនបានអនុញ្ញាត"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"មើលកម្មវិធីច្រើនទៀតដែលអាចចូលប្រើឯកសារទាំងអស់បាន"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ថ្ងៃ}other{# ថ្ងៃ}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ម៉ោង}other{# ម៉ោង}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 នាទី}other{# នាទី}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 វិនាទី}other{# វិនាទី}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ម៉ោង}other{# ម៉ោង}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# នាទី}other{# នាទី}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# វិនាទី}other{# វិនាទី}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ការរំលឹកអំពីការអនុញ្ញាត"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"កម្មវិធីដែលមិនប្រើចំនួន 1"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"កម្មវិធីដែលមិនប្រើចំនួន <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើ <b>រូបថត វីដេអូ តន្ត្រី សំឡេង និងឯកសារផ្សេងទៀត</b>នៅលើឧបករណ៍នេះទេ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើប្រាស់តន្ត្រី និងសំឡេងនៅលើឧបករណ៍នេះទេ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើប្រាស់រូបថត និងវីដេអូនៅលើឧបករណ៍នេះទេ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ផ្ដល់សិទ្ធិឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើរូបថតបន្ថែមទៀតឬ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ថតសំឡេង?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"កម្មវិធីនេះនឹងអាចថតសំឡេង នៅពេលអ្នកកំពុងប្រើប្រាស់កម្មវិធីតែប៉ុណ្ណោះ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ថតសំឡេងឬ?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"បង្ហាញសារ នៅពេលកម្មវិធីចូលប្រើអត្ថបទ រូបភាព ឬខ្លឹមសារផ្សេងទៀតដែលអ្នកបានចម្លង"</string> <string name="show_password_title" msgid="2877269286984684659">"បង្ហាញពាក្យសម្ងាត់"</string> <string name="show_password_summary" msgid="1110166488865981610">"បង្ហាញតួអក្សរមួយភ្លែតខណៈពេលអ្នកវាយបញ្ចូល"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"កម្មវិធីនេះបានបញ្ជាក់ថា វាអាចចែករំលែកទិន្នន័យ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ជាមួយភាគីទីបី"</string> </resources> diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml index 83cc355b3..a6267ed39 100644 --- a/PermissionController/res/values-kn/strings.xml +++ b/PermissionController/res/values-kn/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ಆ್ಯಪ್ ಬಳಕೆಯಲ್ಲಿರುವಾಗ” ಹಾಗೆಯೇ ಇರಿಸಿ"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ಈ ಬಾರಿ ಮಾತ್ರ” ಇರಿಸಿ"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ಹೆಚ್ಚಿನ ಮಾಹಿತಿ"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ಎಲ್ಲಾ ಫೋಟೋಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಅನುಮತಿಸಿ"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ಯಾವುದೇ ರೀತಿಯಲ್ಲೂ ಅನುಮತಿಸಬೇಡಿ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ವಜಾಗೊಳಿಸಿ"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ರಲ್ಲಿ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ದಿನ}one{# ದಿನಗಳು}other{# ದಿನಗಳು}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ನಿಮಿಷ}one{# ನಿಮಿಷಗಳು}other{# ನಿಮಿಷಗಳು}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 ಸೆಕೆಂಡ್}one{# ಸೆಕೆಂಡ್ಗಳು}other{# ಸೆಕೆಂಡ್ಗಳು}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ದಿನ}one{# ದಿನಗಳು}other{# ದಿನಗಳು}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# ನಿಮಿಷ}one{# ನಿಮಿಷಗಳು}other{# ನಿಮಿಷಗಳು}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ಸೆಕೆಂಡ್}one{# ಸೆಕೆಂಡ್ಗಳು}other{# ಸೆಕೆಂಡ್ಗಳು}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ಯಾವುದೇ ಅನುಮತಿ"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ಯಾವುದಾದರೂ ಸಮಯದಲ್ಲಿ"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ಕಳೆದ 7 ದಿನಗಳು"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ಕೊನೆಯ 24 ಗಂಟೆಗಳು"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 ಗಂಟೆಯ ಹಿಂದೆ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ಹಿಂದಿನ 15 ನಿಮಿಷಗಳು"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ಕಳೆದ 1 ನಿಮಿಷ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ಕಳೆದ # ದಿನ}one{ಕಳೆದ # ದಿನಗಳು}other{ಕಳೆದ # ದಿನಗಳು}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ಗಂಟೆಯ ಹಿಂದೆ}one{ಕೊನೆಯ # ಗಂಟೆಗಳು}other{ಕೊನೆಯ # ಗಂಟೆಗಳು}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ಕಳೆದ # ನಿಮಿಷ}one{ಹಿಂದಿನ # ನಿಮಿಷಗಳು}other{ಹಿಂದಿನ # ನಿಮಿಷಗಳು}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ಅನುಮತಿಯ ಬಳಕೆಗಳು ಇಲ್ಲ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿನ ತೀರಾ ಇತ್ತೀಚಿನ ಪ್ರವೇಶ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ಕಳೆದ 7 ದಿನಗಳಲ್ಲಿನ ತೀರಾ ಇತ್ತೀಚಿನ ಪ್ರವೇಶ"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ಕಳೆದ 1 ಗಂಟೆಯಲ್ಲಿನ ಅನುಮತಿಯ ಬಳಕೆ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ಕಳೆದ 15 ನಿಮಿಷಗಳಲ್ಲಿನ ಅನುಮತಿಯ ಬಳಕೆ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ಕಳೆದ 1 ನಿಮಿಷದಲ್ಲಿನ ಅನುಮತಿಯ ಬಳಕೆ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ಕಳೆದ 24 ಗಂಟೆಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ಕಳೆದ 7 ದಿನಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ಕಳೆದ # ದಿನದಲ್ಲಿ ಬಳಸಿಲ್ಲ}one{ಕಳೆದ # ದಿನಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ}other{ಕಳೆದ # ದಿನಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ಕಳೆದ # ಗಂಟೆಯಲ್ಲಿ ಬಳಸಿಲ್ಲ}one{ಕಳೆದ # ಗಂಟೆಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ}other{ಕಳೆದ # ಗಂಟೆಗಳಲ್ಲಿ ಬಳಸಿಲ್ಲ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ಆ್ಯಪ್ ಬಳಸಿದೆ}one{# ಆ್ಯಪ್ಗಳು ಬಳಸಿವೆ}other{# ಆ್ಯಪ್ಗಳು ಬಳಸಿವೆ}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ಎಲ್ಲವನ್ನೂ ಡ್ಯಾಶ್ಬೋರ್ಡ್ನಲ್ಲಿ ವೀಕ್ಷಿಸಿ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ಈ ಆಧಾರದ ಮೇಲೆ ಫಿಲ್ಟರ್ ಮಾಡಲಾಗಿದೆ: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ಮಾಧ್ಯಮಕ್ಕೆ ಮಾತ್ರ ಪ್ರವೇಶಿಸಲು ಅನುಮತಿಸಿ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ಅನುಮತಿಸಿ"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ಆ್ಯಪ್ ಬಳಸುವಾಗ ಮಾತ್ರ ಅನುಮತಿಸಿ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ಎಲ್ಲಾ ಫೋಟೋಗಳನ್ನು ಅನುಮತಿಸಿ"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ಆಯ್ಕೆಮಾಡಿದ ಫೋಟೋಗಳನ್ನು ಅನುಮತಿಸಿ"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ಅನುಮತಿಸಬೇಡಿ"</string> <string name="precise_image_description" msgid="6349638632303619872">"ನಿಖರವಾದ ಸ್ಥಾನ"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ಅನುಮತಿಸಿಲ್ಲದಿರುವುದು"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ಎಲ್ಲಾ ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಬಹುದಾದ ಇನ್ನಷ್ಟು ಆ್ಯಪ್ಗಳನ್ನು ನೋಡಿ"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ದಿನ}one{# ದಿನಗಳು}other{# ದಿನಗಳು}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 ನಿಮಿಷ}one{# ನಿಮಿಷಗಳು}other{# ನಿಮಿಷಗಳು}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 ಸೆಕೆಂಡ್}one{# ಸೆಕೆಂಡ್ಗಳು}other{# ಸೆಕೆಂಡ್ಗಳು}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# ನಿಮಿಷ}one{# ನಿಮಿಷಗಳು}other{# ನಿಮಿಷಗಳು}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ಸೆಕೆಂಡ್}one{# ಸೆಕೆಂಡ್ಗಳು}other{# ಸೆಕೆಂಡ್ಗಳು}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ಅನುಮತಿ ಜ್ಞಾಪನೆಗಳು"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ಬಳಕೆಯಾಗದ ಆ್ಯಪ್"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ಬಳಕೆಯಾಗದ ಆ್ಯಪ್ಗಳು"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ಈ ಸಾಧನದಲ್ಲಿರುವ <b>ಫೋಟೋಗಳು, ವೀಡಿಯೊಗಳು, ಸಂಗೀತ, ಆಡಿಯೋ, ಇತರ ಫೈಲ್ಗಳನ್ನು</b> ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ಈ ಸಾಧನದಲ್ಲಿರುವ ಸಂಗೀತ ಮತ್ತು ಆಡಿಯೊವನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ಈ ಸಾಧನದಲ್ಲಿರುವ ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳಿಗೆ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಆ್ಯಕ್ಸೆಸ್ ನೀಡಬೇಕೇ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ನೀವು ಆ್ಯಪ್ ಬಳಸುತ್ತಿರುವಾಗ ಮಾತ್ರ ಆ್ಯಪ್ಗೆ ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ನೀವು ನಕಲಿಸಿರುವ ಪಠ್ಯ, ಚಿತ್ರಗಳು ಅಥವಾ ಇತರ ವಿಷಯವನ್ನು ಆ್ಯಪ್ಗಳು ಪ್ರವೇಶಿಸಿದಾಗ ಸಂದೇಶವೊಂದನ್ನು ತೋರಿಸಿ"</string> <string name="show_password_title" msgid="2877269286984684659">"ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="show_password_summary" msgid="1110166488865981610">"ನೀವು ಟೈಪ್ ಮಾಡಿದಂತೆ ಅಕ್ಷರಗಳನ್ನು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಪ್ರದರ್ಶಿಸಿ"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ಈ ಆ್ಯಪ್ ಥರ್ಡ್ ಪಾರ್ಟಿಗಳೊಂದಿಗೆ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು ಎಂದು ಉಲ್ಲೇಖಿಸಿದೆ"</string> </resources> diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml index 35a9e1899..81e0e4167 100644 --- a/PermissionController/res/values-ko/strings.xml +++ b/PermissionController/res/values-ko/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\'앱 사용 중에만 허용\' 유지"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\'이번만 허용\' 유지"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"추가 정보"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"모든 사진에 대한 액세스 허용"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"사진 선택"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"추가 사진 선택"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"추가 사진 선택 안함"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"무시하고 허용 안함"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"닫기"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1일}other{#일}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1시간}other{#시간}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1분}other{#분}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1초}other{#초}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{#일}other{#일}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{#시간}other{#시간}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{#분}other{#분}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{#초}other{#초}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"모든 권한"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"전체 기간"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"최근 7일"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"최근 24시간"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"최근 1시간"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"최근 15분"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"최근 1분"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{지난 #일}other{지난 #일}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{최근 #시간}other{최근 #시간}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{최근 #분}other{최근 #분}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"사용 권한 없음"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"가장 최근에 액세스한 앱"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"지난 7일 동안 가장 최근에 액세스한 앱"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"지난 1시간 동안 사용된 권한"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"지난 15분 동안 사용된 권한"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"지난 1분 동안 사용된 권한"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"지난 24시간 이내에 사용하지 않음"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"지난 7일 이내에 사용하지 않음"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{지난 #일 동안 사용하지 않음}other{지난 #일 동안 사용하지 않음}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{지난 #시간 동안 사용하지 않음}other{지난 #시간 동안 사용하지 않음}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{앱 1개에서 사용}other{앱 #개에서 사용}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"대시보드에서 모두 보기"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"필터링 기준: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"미디어 액세스만 허용"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"항상 허용"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"앱 사용 중에만 허용"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"모든 사진 허용"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"선택한 사진 허용"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"항상 확인"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"허용 안함"</string> <string name="precise_image_description" msgid="6349638632303619872">"정확한 위치"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"허용되지 않음"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"모든 파일에 액세스할 수 있는 앱 더보기"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1일}other{#일}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1시간}other{#시간}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1분}other{#분}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1초}other{#초}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{#시간}other{#시간}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{#분}other{#분}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{#초}other{#초}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"권한 알림"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"사용하지 않는 앱 1개"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"사용하지 않는 앱 <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>개"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 기기의 <b>사진, 동영상, 음악, 오디오, 기타 파일</b>에 액세스하도록 허용하시겠습니까?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 기기의 음악과 오디오에 액세스하도록 허용하시겠습니까?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 기기의 사진과 동영상에 액세스하도록 허용하시겠습니까?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에 추가 사진에 대한 액세스를 허용하시겠습니까?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 오디오를 녹음하도록 허용하시겠습니까?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"앱을 사용하고 있는 동안에만 앱에서 오디오를 녹음할 수 있습니다."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 오디오를 녹음하도록 허용하시겠습니까?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"앱이 복사된 텍스트, 이미지 또는 기타 콘텐츠에 액세스할 때 메시지 표시"</string> <string name="show_password_title" msgid="2877269286984684659">"비밀번호 표시"</string> <string name="show_password_summary" msgid="1110166488865981610">"입력할 때 잠깐 표시"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"앱에서 <xliff:g id="PERMISSION_NAME">%s</xliff:g> 데이터를 서드 파티와 공유할 수 있다고 명시했습니다."</string> </resources> diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml index d65b5b961..249831640 100644 --- a/PermissionController/res/values-ky/strings.xml +++ b/PermissionController/res/values-ky/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Колдонмо колдонулуп жатканда\" режими кала берсин"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Ушул жолу гана\" жөндөөсү калсын"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дагы маалымат"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Бардык сүрөттөргө кирүүгө уруксат берүү"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Cүрөттөрдү тандаңыз"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Дагы сүрөт тандоо"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Дагы сүрөттөр тандалбасын"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Баары бир тыюу салуу"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Жабуу"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> уруксаттын ичинен <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 күн}other{# күн}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 саат}other{# саат}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мүн.}other{# мүн.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 сек.}other{# сек.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# күн}other{# күн}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# саат}other{# саат}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мүн.}other{# мүн.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# сек.}other{# сек.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Бардык уруксаттар"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Каалаган убакта"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Акыркы 7 күндө"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Акыркы 24 саатта"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Акыркы 1 саатта"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Акыркы 15 мүнөттө"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Акыркы 1 мүнөт"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Акыркы # күндө}other{Акыркы # күндө}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Акыркы # саатта}other{Акыркы # саатта}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Акыркы # мүнөттө}other{Акыркы # мүнөттө}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Уруксаттар колдонулган жок"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Бардык убакыттагы эң акыркы колдонулушу"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Өткөн 7 күндүн ичиндеги эң акыркы колдонулушу"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Акыркы 1 саатта колдонулган уруксаттар"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Акыркы 15 мүнөттө уруксаттардын колдонулушу"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Акыркы 1 мүнөттө уруксаттардын колдонулушу"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Акыркы 24 сааттын ичинде колдонулган жок"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Акыркы 7 күндө колдонулган жок"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Акыркы # күндө колдонулган жок}other{Акыркы # күндө колдонулган жок}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Акыркы # саатта колдонулган жок}other{Акыркы # саатта колдонулган жок}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 колдонмо пайдаланды}other{# колдонмо пайдаланды}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Баарын Куралдар тактасында көрүү"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Чыпка: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Медиа файлдарга гана уруксат берүү"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Бардык учурда уруксат берилет"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Колдонмо колдонулуп жатканда гана уруксат берилет"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Бардык сүрөттөргө уруксат берүү"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Тандалган сүрөттөргө уруксат берүү"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Ар дайым суралсын"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Жок"</string> <string name="precise_image_description" msgid="6349638632303619872">"Так жайгашкан жери"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Тыюу салынган"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Дагы кайсы колдонмолорго бардык файлдар жеткиликтүү?"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 күн}other{# күн}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 саат}other{# саат}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 мүнөт}other{# мүнөт}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунд}other{# секунд}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# саат}other{# саат}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# мүнөт}other{# мүнөт}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунд}other{# секунд}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Уруксат жөнүндө эстеткичтер"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 колдонулбаган колдонмо"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> колдонулбаган колдонмо"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна ушул түзмөктөгү <b>сүрөттөрдү, видеолорду, ырларды, аудио файлдарды жана башка нерселерди</b> жеткиликтүү кыласызбы?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна ушул түзмөктөгү ырлар менен аудио файлдарды жеткиликтүү кыласызбы?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна ушул түзмөктөгү сүрөттөр менен видеолорду жеткиликтүү кыласызбы?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна көбүрөөк сүрөттөргө кирүүгө уруксат берилсинби?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна аудио файлдарды жаздырганга уруксат бересизби?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Колдонмону колдонуп жатканда гана, ал аудио жаздыра алат"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна аудио файлдарды жаздырууга уруксат бересизби?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Колдонмолор көчүрүлгөн текстти, сүрөттөрдү же башка нерселерди пайдаланганда билдирүүлөр көрүнөт"</string> <string name="show_password_title" msgid="2877269286984684659">"Сырсөз көрүнсүн"</string> <string name="show_password_summary" msgid="1110166488865981610">"Сырсөз терилип жатканда символдор бир саамга көрүнөт"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Бул колдонмо <xliff:g id="PERMISSION_NAME">%s</xliff:g> тууралуу маалыматты үчүнчү тараптар менен бөлүшүүгө уруксат сурап жатат"</string> </resources> diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml index 22609be0a..c2de2b955 100644 --- a/PermissionController/res/values-lo/strings.xml +++ b/PermissionController/res/values-lo/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"ໃຊ້ “ໃນຂະນະທີ່ມີການໃຊ້ແອັບຢູ່”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"ໃຊ້ແບບ “ສະເພາະເທື່ອນີ້” ຕໍ່ໄປ"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ຂໍ້ມູນເພີ່ມເຕີມ"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ອະນຸຍາດສິດເຂົ້າເຖິງຮູບພາບທັງໝົດ"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ເລືອກຮູບພາບ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ເລືອກຮູບພາບເພີ່ມເຕີມ"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ຢ່າເລືອກຮູບພາບເພີ່ມເຕີມ"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ຢືນຢັນບໍ່ອະນຸຍາດ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ປິດໄວ້"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ຈາກທັງໝົດ <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ມື້}other{# ມື້}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ນທ}other{# ນທ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 ວິ}other{# ວິ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ມື້}other{# ມື້}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# ນທ}other{# ນທ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ວິ}other{# ວິ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ສິດອະນຸຍາດໃດກໍໄດ້"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ເວລາໃດກໍໄດ້"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 ມື້ທີ່ຜ່ານມາ"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 ຊົ່ວໂມງທີ່ຜ່ານມາ"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 ຊົ່ວໂມງທີ່ຜ່ານມາ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 ນາທີຜ່ານມາ"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 ນາທີທີ່ຜ່ານມາ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# ມື້ທີ່ຜ່ານມາ}other{# ມື້ທີ່ຜ່ານມາ}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ຊົ່ວໂມງທີ່ຜ່ານມາ}other{# ຊົ່ວໂມງທີ່ຜ່ານມາ}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# ນາທີທີ່ຜ່ານມາ}other{# ນາທີທີ່ຜ່ານມາ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ບໍ່ມີການນຳໃຊ້ສິດອະນຸຍາດ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ການເຂົ້າເຖິງຫຼ້າສຸດຕອນໃດກໍໄດ້"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ເຂົ້າເຖິງຫຼ້າສຸດໃນ 7 ມື້ທີ່ຜ່ານມາ"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ການນຳໃຊ້ສິດອະນຸຍາດໃນ 1 ຊົ່ວໂມງທີ່ຜ່ານມາ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ການນຳໃຊ້ສິດອະນຸຍາດໃນ 15 ນາທີທີ່ຜ່ານມາ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ການນຳໃຊ້ສິດອະນຸຍາດໃນ 1 ນາທີທີ່ຜ່ານມາ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ບໍ່ໄດ້ໃຊ້ໃນ 24 ຊົ່ວໂມງທີ່ຜ່ານມາ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ບໍ່ໄດ້ໃຊ້ໃນ 7 ມື້ທີ່ຜ່ານມາ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ບໍ່ໄດ້ໃຊ້ໃນ # ມື້ທີ່ຜ່ານມາ}other{ບໍ່ໄດ້ໃຊ້ໃນ # ມື້ທີ່ຜ່ານມາ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ບໍ່ໄດ້ໃຊ້ໃນ # ຊົ່ວໂມງທີ່ຜ່ານມາ}other{ບໍ່ໄດ້ໃຊ້ໃນ # ຊົ່ວໂມງທີ່ຜ່ານມາ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{ໃຊ້ໂດຍ 1 ແອັບ}other{ໃຊ້ໂດຍ # ແອັບ}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ເບິ່ງທັງໝົດໃນແຜງໜ້າປັດ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ກັ່ນຕອງແລ້ວໂດຍ: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ອະນຸຍາດການເຂົ້າເຖິງມີເດຍເທົ່ານັ້ນ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ອະນຸຍາດຕະຫຼອດເວລາ"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ອະນຸຍາດໃນເວລາທີ່ກຳລັງໃຊ້ແອັບເທົ່ານັ້ນ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ອະນຸຍາດຮູບພາບທັງໝົດ"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ອະນຸຍາດຮູບພາບທີ່ເລືອກໄວ້"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ຖາມທຸກເທື່ອ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ບໍ່ອະນຸຍາດ"</string> <string name="precise_image_description" msgid="6349638632303619872">"ສະຖານທີ່ແບບລະອຽດ"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ບໍ່ອະນຸຍາດ"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ເບິ່ງແອັບເພີ່ມເຕີມທີ່ສາມາດເຂົ້າເຖິງໄຟລ໌ທັງໝົດໄດ້"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ມື້}other{# ມື້}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 ນາທີ}other{# ນາທີ}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 ວິນາທີ}other{# ວິນາທີ}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# ນາທີ}other{# ນາທີ}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ວິນາທີ}other{# ວິນາທີ}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ການແຈ້ງເຕືອນການອະນຸຍາດ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ອະນຸຍາດໃຫ້ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ເຂົ້າເຖິງ <b>ຮູບພາບ, ວິດີໂອ, ເພງ, ສຽງ ແລະ ໄຟລ໌ອື່ນໆ</b> ຢູ່ອຸປະກອນນີ້ບໍ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ອະນຸຍາດໃຫ້ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ເຂົ້າເຖິງເພງ ແລະ ສຽງຢູ່ອຸປະກອນນີ້ບໍ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ອະນຸຍາດໃຫ້ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອຢູ່ອຸປະກອນນີ້ບໍ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ໃຫ້ສິດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ສິດເຂົ້າເຖິງຮູບພາບເພີ່ມເຕີມບໍ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ອະນຸຍາດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ໃຫ້ບັນທຶກສຽງບໍ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ແອັບນີ້ສາມາດບັນທຶກສຽງໃນຂະນະທີ່ທ່ານກຳລັງໃຊ້ແອັບເທົ່ານັ້ນ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ອະນຸຍາດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ໃຫ້ບັນທຶກສຽງບໍ?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ສະແດງຂໍ້ຄວາມເມື່ອແອັບເຂົ້າເຖິງຂໍ້ຄວາມ, ຮູບພາບ ຫຼື ເນື້ອຫາອື່ນທີ່ທ່ານສຳເນົາໄວ້"</string> <string name="show_password_title" msgid="2877269286984684659">"ສະແດງລະຫັດຜ່ານ"</string> <string name="show_password_summary" msgid="1110166488865981610">"ສະແດງຕົວອັກສອນເປັນເວລາສັ້ນໆໃນເວລາພິມ"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ແອັບນີ້ລະບຸວ່າແອັບອາດແບ່ງປັນຂໍ້ມູນ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ກັບພາກສ່ວນທີສາມ"</string> </resources> diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml index 0e6f08683..6d1330719 100644 --- a/PermissionController/res/values-lt/strings.xml +++ b/PermissionController/res/values-lt/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Išlaikyti režimą „Kai programa naudojama“ įjungtą"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Palikti „Tik šį kartą“"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daugiau inform."</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Leisti pasiekti visas nuotraukas"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pasirinkti nuotraukas"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pasirinkti daugiau nuotraukų"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nesirinkti daugiau nuotraukų"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Vis tiek neleisti"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Atmesti"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> iš <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • „<xliff:g id="APP_NAME">%2$s</xliff:g>“ • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 diena}one{# diena}few{# dienos}many{# dienos}other{# dienų}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 minutė}one{# minutė}few{# minutės}many{# minutės}other{# minučių}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sekundė}one{# sekundė}few{# sekundės}many{# sekundės}other{# sekundžių}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# diena}one{# diena}few{# dienos}many{# dienos}other{# dienų}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# minutė}one{# minutė}few{# minutės}many{# minutės}other{# minučių}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sekundė}one{# sekundė}few{# sekundės}many{# sekundės}other{# sekundžių}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Bet koks leidimas"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Bet koks laikas"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Pastarosios 7 dienos"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Pastarosios 24 valandos"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Pastaroji 1 valanda"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Pastarosios 15 minučių"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Pastaroji minutė"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Pastaroji # diena}one{Pastaroji # diena}few{Pastarosios # dienos}many{Pastarosios # dienos}other{Pastarųjų # dienų}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Pastaroji # valanda}one{Pastaroji # valanda}few{Pastarosios # valandos}many{Pastarosios # valandos}other{Pastarųjų # valandų}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Pastaroji # minutė}one{Pastaroji # minutė}few{Pastarosios # minutės}many{Pastarosios # minutės}other{Pastarųjų # minučių}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Leidimai nenaudoti"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Paskutinį kartą pasiekta bet kuriuo metu"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Paskutinį kartą pasiekta per pastarąsias 7 dienas"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Leidimo naudojimas per pastarąją 1 valandą"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Leidimo naudojimas per pastarąsias 15 minučių"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Leidimo naudojimas per pastarąją 1 minutę"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nenaudota per pastarąsias 24 val."</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nenaudota per pastarąsias septynias dienas"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nenaudota per pastarąją # dieną}one{Nenaudota per pastarąją # dieną}few{Nenaudota per pastarąsias # dienas}many{Nenaudota per pastarosios # dienos}other{Nenaudota per pastarąsias # dienų}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nenaudota per pastarąją # valandą}one{Nenaudota per pastarąją # valandą}few{Nenaudota per pastarąsias # valandas}many{Nenaudota per pastarosios # valandos}other{Nenaudota per pastarąsias # valandų}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Naudojama 1 programoje}one{Naudojama # programoje}few{Naudojama # programose}many{Naudojama # programos}other{Naudojama # programų}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Žr. viską informacijos suvestinėje"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtruota pagal: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Leisti pasiekti tik mediją"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Leisti visą laiką"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Leisti tik naudojant programą"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Leisti visas nuotraukas"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Leisti pasirinktas nuotraukas"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Klausti kaskart"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Neleisti"</string> <string name="precise_image_description" msgid="6349638632303619872">"Tiksli vietovė"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Neleidžiama"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Peržiūrėti daugiau programų, galinčių pasiekti visus failus"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 diena}one{# diena}few{# dienos}many{# dienos}other{# dienų}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minutė}one{# minutė}few{# minutės}many{# minutės}other{# minučių}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekundė}one{# sekundė}few{# sekundės}many{# sekundės}other{# sekundžių}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minutė}one{# minutė}few{# minutės}many{# minutės}other{# minučių}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekundė}one{# sekundė}few{# sekundės}many{# sekundės}other{# sekundžių}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Leidimų priminimai"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nenaudojama programa"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Nenaudojamų programų: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pasiekti <b>nuotrauk., vaizdo, garso įrašus, muziką, kitus failus</b> įrenginyje?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pasiekti muziką ir garso failus šiame įrenginyje?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pasiekti nuotraukas ir vaizdo įrašus šiame įrenginyje?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pasiekti daugiau nuotraukų?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> įrašyti garsą?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programa galės įrašyti garsą, tik kai ją naudosite"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Leisti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> įrašyti garsą?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Rodyti pranešimą, kai programos pasiekia nukopijuotą tekstą, vaizdus ar kitą turinį"</string> <string name="show_password_title" msgid="2877269286984684659">"Rodyti slaptažodžius"</string> <string name="show_password_summary" msgid="1110166488865981610">"Trumpai rodyti simbolius vedant tekstą"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ši programa nurodė, kad gali bendrinti <xliff:g id="PERMISSION_NAME">%s</xliff:g> duomenis su trečiosiomis šalimis"</string> </resources> diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml index 9cfb9c087..f51bbeb28 100644 --- a/PermissionController/res/values-lv/strings.xml +++ b/PermissionController/res/values-lv/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Paturēt atļauju “Kamēr lietotne tiek izmantota”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Paturēt iestatījumu “Tikai šoreiz”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Informācija"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Atļaut piekļuvi visiem fotoattēliem"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Atlasīt fotoattēlus"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Atlasīt vairāk fotoattēlu"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Neatlasīt citus fotoattēlus"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tomēr neatļaut"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Nerādīt"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. no <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 diena}zero{# dienu}one{# diena}other{# dienas}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 stunda}zero{# stundu}one{# stunda}other{# stundas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}zero{# min}one{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}zero{# s}one{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# diena}zero{# dienu}one{# diena}other{# dienas}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# stunda}zero{# stundu}one{# stunda}other{# stundas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}zero{# min}one{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}zero{# s}one{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Jebkura atļauja"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Jebkurā laikā"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Pēdējās 7 dienās"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Pēdējās 24 stundās"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Pēdējā stundā"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Pēdējās 15 minūtēs"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Pēdējā minūtē"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Pēdējā # dienā}zero{Pēdējās # dienās}one{Pēdējā # dienā}other{Pēdējās # dienās}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Pēdējā # stundā}zero{Pēdējās # stundās}one{Pēdējā # stundā}other{Pēdējās # stundās}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Pēdējā # minūtē}zero{Pēdējās # minūtēs}one{Pēdējā # minūtē}other{Pēdējās # minūtēs}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nav lietota neviena atļauja"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Nesenākā piekļuve jebkurā laikā"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Nesenākā piekļuve pēdējo 7 dienu laikā"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Atļauju lietojums pēdējās stundas laikā"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Atļauju lietojums pēdējās 15 minūtēs"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Atļauju lietojums pēdējā minūtē"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Pēdējo 24 stundu laikā nav izmantota"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Pēdējo 7 dienu laikā nav izmantota"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Pēdējās # dienas laikā nav izmantota}zero{Pēdējo # dienu laikā nav izmantota}one{Pēdējās # dienas laikā nav izmantota}other{Pēdējo # dienu laikā nav izmantota}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Pēdējās # stundas laikā nav izmantota}zero{Pēdējo # stundu laikā nav izmantota}one{Pēdējās # stundas laikā nav izmantota}other{Pēdējo # stundu laikā nav izmantota}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Izmanto 1 lietotne}zero{Izmanto # lietotnes}one{Izmanto # lietotne}other{Izmanto # lietotnes}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Skatīt visu rīkā Permission Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrēts pēc: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Atļaut piekļūt tikai multivides failiem"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vienmēr atļaut"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Atļaut tikai lietotnes izmantošanas laikā"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Atļaut visus fotoattēlus"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Atļaut atlasītos fotoattēlus"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Vaicāt katru reizi"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Neatļaut"</string> <string name="precise_image_description" msgid="6349638632303619872">"Precīza atrašanās vieta"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nav atļauts"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Skatīt citas lietotnes, kas drīkst piekļūt visiem failiem"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 diena}zero{# dienu}one{# diena}other{# dienas}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 stunda}zero{# stundu}one{# stunda}other{# stundas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minūte}zero{# minūšu}one{# minūte}other{# minūtes}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunde}zero{# sekunžu}one{# sekunde}other{# sekundes}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# stunda}zero{# stundu}one{# stunda}other{# stundas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minūte}zero{# minūšu}one{# minūte}other{# minūtes}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunde}zero{# sekunžu}one{# sekunde}other{# sekundes}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Atgādinājumi par atļauju"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 neizmantota lietotne"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> neizmantotas lietotnes"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt <b>foto, video, mūzikai, audio u.c. failiem</b> ierīcē?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt mūzikai un audio failiem šajā ierīcē?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt fotoattēliem un video šajā ierīcē?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vai piešķirt lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļuvi citiem fotoattēliem?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ierakstīt audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Lietotne varēs ierakstīt audio tikai tad, kad izmantosiet lietotni."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ierakstīt audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Rādīt ziņojumu, kad lietotnes piekļūst jūsu nokopētajam tekstam, attēliem vai citam saturam"</string> <string name="show_password_title" msgid="2877269286984684659">"Rādīt paroles"</string> <string name="show_password_summary" msgid="1110166488865981610">"Rakstot tiek īslaicīgi rādītas rakstzīmes"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Lietotne norādīja, ka tā var kopīgot ar trešajām pusēm šādus datus: <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string> </resources> diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml index 514ae451b..f33e83eba 100644 --- a/PermissionController/res/values-mk/strings.xml +++ b/PermissionController/res/values-mk/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Задржи ја „Додека се користи апликацијата“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Задржи „Само овој пат“"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Уште информации"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дозволете пристап до сите фотографии"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Изберете фотографии"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Изберете повеќе фотографии"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Не избирајте повеќе фотографии"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Сепак не дозволувај"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Отфрли"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ден}one{# ден}other{# дена}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 час}one{# час}other{# часа}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мин.}one{# мин.}other{# мин.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 сек.}one{# сек.}other{# сек.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ден}one{# ден}other{# дена}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# час}one{# час}other{# часа}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мин.}one{# мин.}other{# мин.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# сек.}one{# сек.}other{# сек.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Која било дозвола"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Кога било"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Минатите 7 дена"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Минатите 24 часа"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Минатиот час"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Минатите 15 минути"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Последна 1 минута"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Минатиот # ден}one{Минатите # ден}other{Минатите # дена}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Минатиот # час}one{Минатите # час}other{Минатите # часа}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Минатата # минута}one{Минатите # минута}other{Минатите # минути}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Не се користени дозволи"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Најнов пристап во кое било време"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Најнов пристап во минатите 7 дена"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Користење дозволи во последниот 1 час"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Користење дозволи во минатите 15 минути"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Користење дозволи во последната 1 минута"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Не е користена во минатите 24 часа"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Не е користена во минатите 7 дена"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Не е користена во минатиот # ден}one{Не е користена во минатите # ден}other{Не е користена во минатите # дена}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Не е користена во минатиот # час}one{Не е користена во минатите # час}other{Не е користена во минатите # часа}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Искористена од 1 апликација}one{Искористена од # апликација}other{Искористена од # апликации}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Прикажи ги сите на контролната табла"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Филтрирано според: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дозволи пристап само до аудиовизуелните содржини"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволи цело време"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозволи само додека се користи апликацијата"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дозволете ги сите фотографии"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дозволете избрани фотографии"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Прашувај секогаш"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволувај"</string> <string name="precise_image_description" msgid="6349638632303619872">"Прецизна локација"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Без дозвола"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Видете уште апликации што може да пристапат до сите датотеки"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ден}one{# ден}other{# дена}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 час}one{# час}other{# часа}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минута}one{# минута}other{# минути}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}one{# секунда}other{# секунди}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}one{# час}other{# часа}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минута}one{# минута}other{# минути}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}one{# секунда}other{# секунди}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Потсетници за дозволата"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 некористена апликација"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> некористени апликации"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да пристапува до <b>фотографии, видеа, музика, аудио и други датотеки</b> на уредов?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да пристапува до музика и аудиодатотеки на уредов?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да пристапува до фотографии и видеа на уредов?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Да се дозволи пристап на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> до повеќе фотографии?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да снима аудио?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Апликацијава ќе може да снима аудио само додека ја користите"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да снима аудио?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Нека се прикажува известување кога апликациите пристапуваат до текст, слики или други содржини што сте ги копирале"</string> <string name="show_password_title" msgid="2877269286984684659">"Прикажувај ги лозинките"</string> <string name="show_password_summary" msgid="1110166488865981610">"Прикажувај ги знаците накратко додека пишувам"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Апликацијава изјави дека можеби ќе споделува податоци за <xliff:g id="PERMISSION_NAME">%s</xliff:g> со трети страни"</string> </resources> diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml index 8a328b4f6..fd554f934 100644 --- a/PermissionController/res/values-ml/strings.xml +++ b/PermissionController/res/values-ml/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ആപ്പ് ഉപയോഗത്തിലിരിക്കുമ്പോൾ” തുടരുക"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ഇപ്പോഴത്തേക്ക് മാത്രം” നിലനിർത്തുക"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"കൂടുതൽ വിവരങ്ങൾ"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"എന്തായാലും അനുവദിക്കരുത്"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ഡിസ്മിസ് ചെയ്യുക"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-ൽ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> എണ്ണം"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{ഒരു ദിവസം}other{# ദിവസം}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{ഒരു മണിക്കൂർ}other{# മണിക്കൂർ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{ഒരു മിനിറ്റ്}other{# മിനിറ്റ്}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{ഒരു സെക്കൻഡ്}other{# സെക്കൻഡ്}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ദിവസം}other{# ദിവസം}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# മണിക്കൂർ}other{# മണിക്കൂർ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# മിനിറ്റ്}other{# മിനിറ്റ്}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# സെക്കൻഡ്}other{# സെക്കൻഡ്}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ഏതെങ്കിലും അനുമതി"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ഏത് സമയത്തും"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"കഴിഞ്ഞ 7 ദിവസം"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"അവസാന 24 മണിക്കൂർ"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"കഴിഞ്ഞ ഒരു മണിക്കൂര്"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"കഴിഞ്ഞ 15 മിനിറ്റ്"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"അവസാന ഒരു മിനിറ്റ്"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{അവസാന # ദിവസം}other{അവസാന # ദിവസം}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{അവസാന # മണിക്കൂർ}other{അവസാന # മണിക്കൂർ}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{അവസാന # മിനിറ്റ്}other{അവസാന # മിനിറ്റ്}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"അനുമതി ഉപയോഗങ്ങളൊന്നുമില്ല"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ഏതുസമയത്തുമുള്ള ഏറ്റവും സമീപകാല ആക്സസ്"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"കഴിഞ്ഞ 7 ദിവസത്തിലെ ഏറ്റവും സമീപകാല ആക്സസ്"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"കഴിഞ്ഞ 1 മണിക്കൂറിലെ അനുമതിയുടെ ഉപയോഗം"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"കഴിഞ്ഞ 15 മിനിറ്റിലെ അനുമതിയുടെ ഉപയോഗം"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"കഴിഞ്ഞ 1 മിനിറ്റിലെ അനുമതിയുടെ ഉപയോഗം"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"കഴിഞ്ഞ 24 മണിക്കൂറിനിടെ ഉപയോഗിച്ചിട്ടില്ല"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"കഴിഞ്ഞ 7 ദിവസത്തിനിടെ ഉപയോഗിച്ചിട്ടില്ല"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{കഴിഞ്ഞ # ദിവസത്തിനിടെ ഉപയോഗിച്ചിട്ടില്ല}other{കഴിഞ്ഞ # ദിവസത്തിനിടെ ഉപയോഗിച്ചിട്ടില്ല}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{കഴിഞ്ഞ # മണിക്കൂറിനിടെ ഉപയോഗിച്ചിട്ടില്ല}other{കഴിഞ്ഞ # മണിക്കൂറിനിടെ ഉപയോഗിച്ചിട്ടില്ല}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{ഒരു ആപ്പ് ഉപയോഗിച്ചു}other{# ആപ്പുകൾ ഉപയോഗിച്ചു}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"എല്ലാം ഡാഷ്ബോർഡിൽ കാണുക"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ഇതുപ്രകാരം ഫിൽട്ടർ ചെയ്തു: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"മീഡിയ ഫയലുകളിലേക്ക് മാത്രം ആക്സസ് അനുവദിക്കുക"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ഏതുസമയത്തും അനുവദിക്കുക"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"എപ്പോഴും ചോദിക്കുക"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"അനുവദിക്കരുത്"</string> <string name="precise_image_description" msgid="6349638632303619872">"കൃത്യമായ ലൊക്കേഷൻ"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"അനുവദിച്ചിട്ടില്ല"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"എല്ലാ ഫയലുകളും ആക്സസ് ചെയ്യാൻ കഴിയുന്ന കൂടുതൽ ആപ്പുകൾ കാണുക"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{ഒരു ദിവസം}other{# ദിവസം}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{ഒരു മണിക്കൂർ}other{# മണിക്കൂർ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{ഒരു മിനിറ്റ്}other{# മിനിറ്റ്}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{ഒരു സെക്കൻഡ്}other{# സെക്കൻഡ്}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# മണിക്കൂർ}other{# മണിക്കൂർ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# മിനിറ്റ്}other{# മിനിറ്റ്}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# സെക്കൻഡ്}other{# സെക്കൻഡ്}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"അനുമതിയ്ക്കുള്ള റിമൈൻഡറുകൾ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"ഒരു ഉപയോഗിക്കാത്ത ആപ്പ്"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ഉപയോഗിക്കാത്ത ആപ്പുകൾ"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b>ഫോട്ടോ, വീഡിയോ, സംഗീതം, ഓഡിയോ, മറ്റ് ഫയലുകൾ</b> എന്നിവയിലേക്ക് <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> എന്നതിന് ആക്സസ് നൽകണോ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ഈ ഉപകരണത്തിലെ സംഗീതവും ഓഡിയോയും ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> എന്നതിനെ അനുവദിക്കണോ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ഈ ഉപകരണത്തിലെ ഫോട്ടോകളും വീഡിയോകളും ആക്സസ് ചെയ്യാൻ <b> <xliff:g id="APP_NAME">%1$s</xliff:g></b> എന്നതിനെ അനുവദിക്കണോ?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രമേ അതിന് ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ കഴിയൂ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"നിങ്ങൾ പകർത്തിയ ടെക്സ്റ്റോ ചിത്രങ്ങളോ മറ്റ് ഉള്ളടക്കമോ ആപ്പുകൾ ആക്സസ് ചെയ്യുമ്പോൾ ഒരു സന്ദേശം കാണിക്കുക"</string> <string name="show_password_title" msgid="2877269286984684659">"പാസ്വേഡുകൾ കാണിക്കുക"</string> <string name="show_password_summary" msgid="1110166488865981610">"ടൈപ്പ് ചെയ്യുന്ന അക്ഷരങ്ങൾ പ്രദർശിപ്പിക്കുക"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml index f16b8bb83..5f9d171c6 100644 --- a/PermissionController/res/values-mn/strings.xml +++ b/PermissionController/res/values-mn/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Аппыг ашиглаж байх үед\" хэвээр үлдээх"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Зөвхөн энэ удаад зөвшөөрөх”-г хэвээр хадгалах"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дэлгэрэнгүй мэдээлэл"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Бүх зурагт хандахыг зөвшөөрөх"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Зураг сонгох"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Илүү олон зураг сонгох"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Илүү олон зураг сонгохгүй"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ямартай ч бүү зөвшөөр"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Үл хэрэгсэх"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-н <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 өдөр}other{# өдөр}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 цаг}other{# цаг}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мин}other{# мин}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 сек}other{# сек}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# хоног}other{# хоног}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# цаг}other{# цаг}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мин}other{# мин}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# сек}other{# сек}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Дурын зөвшөөрөл"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Дурын хугацаа"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Сүүлийн 7 хоног"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Сүүлийн 24 цаг"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Сүүлийн 1 цаг"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Сүүлийн 15 минут"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Сүүлийн 1 минут"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Сүүлийн # хоног}other{Сүүлийн # хоног}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Сүүлийн # цаг}other{Сүүлийн # цаг}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Сүүлийн # минут}other{Сүүлийн # минут}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Зөвшөөрлийн хэрэглээ алга"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Бүх цаг үеийн хамгийн сүүлийн хандалт"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Сүүлийн 7 хоногт хамгийн сүүлд хийсэн хандалт"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Сүүлийн 1 цагийн зөвшөөрлийн ашиглалт"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Сүүлийн 15 минутын зөвшөөрлийн ашиглалт"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Сүүлийн 1 минутын зөвшөөрлийн ашиглалт"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Өнгөрсөн 24 цагт ашиглаагүй"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Өнгөрсөн 7 хоногт ашиглаагүй"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Сүүлийн # хоногт ашиглаагүй}other{Сүүлийн # хоногт ашиглаагүй}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Сүүлийн # цагт ашиглаагүй}other{Сүүлийн # цагт ашиглаагүй}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 апп ашигласан}other{# апп ашигласан}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Хяналтын самбараас бүгдийг нь харах"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Шүүсэн: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Зөвхөн медиад хандахыг зөвшөөрөх"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Ямар ч үед зөвшөөрөх"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Зөвхөн аппыг ашиглаж байх үед зөвшөөрөх"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Бүх зургийг зөвшөөрөх"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Сонгосон зургуудыг зөвшөөрөх"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Тухай бүрд асуух"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Бүү зөвшөөр"</string> <string name="precise_image_description" msgid="6349638632303619872">"Нарийвчилсан байршил"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Зөвшөөрөөгүй"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Бүх файлд хандах боломжтой илүү их аппыг харах"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 өдөр}other{# өдөр}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 цаг}other{# цаг}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минут}other{# минут}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунд}other{# секунд}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# цаг}other{# цаг}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минут}other{# минут}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунд}other{# секунд}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Зөвшөөрлийн сануулагч"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Ашиглаагүй 1 апп"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Ашиглаагүй <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> апп"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д энэ төхөөрөмжийн <b>зураг, видео, хөгжим, аудио, бусад файлд</b> хандахыг зөвшөөрөх үү?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д энэ төхөөрөмж дээрх хөгжим болон аудионд хандахыг зөвшөөрөх үү?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д энэ төхөөрөмж дээрх зураг болон видеонд хандахыг зөвшөөрөх үү?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д илүү олон зурагт хандах эрх өгөх үү?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д аудио бичихийг зөвшөөрөх үү?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Энэ апп зөвхөн таныг ашиглаж байх үед л аудио бичих боломжтой болно"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д аудио бичихийг зөвшөөрөх үү?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Аппууд таны хуулсан текст, зураг эсвэл бусад контентод хандах үед мессеж харуулах"</string> <string name="show_password_title" msgid="2877269286984684659">"Нууц үгнүүдийг харуулах"</string> <string name="show_password_summary" msgid="1110166488865981610">"Таныг бичиж явцад тэмдэгтүүдийг түр үзүүлэх"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Энэ апп <xliff:g id="PERMISSION_NAME">%s</xliff:g>-н өгөгдлийг гуравдагч талуудтай хуваалцаж болохыг мэдэгдсэн"</string> </resources> diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml index 242d32857..5f51f979b 100644 --- a/PermissionController/res/values-mr/strings.xml +++ b/PermissionController/res/values-mr/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ॲप वापरत असताना” ठेवा"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“फक्त यावेळेपुरते” ठेवा"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"अधिक माहिती"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"सर्व फोटो ॲक्सेस करण्याची अनुमती द्या"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"फोटो निवडा"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"आणखी फोटो निवडा"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"आणखी फोटो निवडू नका"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"तरीही अनुमती देऊ नका"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"डिसमिस करा"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> पैकी <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{एक दिवस}other{# दिवस}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{एक तास}other{# तास}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{एक मिनिट}other{# मिनिटे}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{एक सेकंद}other{# सेकंद}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# दिवस}other{# दिवस}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# तास}other{# तास}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# मिनिट}other{# मिनिटे}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# सेकंद}other{# सेकंद}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"कोणतीही परवानगी"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"कधीही"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"शेवटचे सात दिवस"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"गेल्या २४ तासात"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"शेवटचा एक तास"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"शेवटची १५ मिनिटे"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"शेवटचा एक मिनिट"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{शेवटचा # दिवस}other{शेवटचे # दिवस}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{मागील # तासामधील}other{मागील # तासांमधील}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{मागील # मिनिटामधील}other{मागील # मिनिटांमधील}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"वापराची परवानगी नाही"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"कोणत्याही वेळी सर्वात अलीकडील अॅक्सेस"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"मागील सात दिवसांतील सर्वात अलीकडील अॅक्सेस"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"मागील एका तासातील परवानगी वापर"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"मागील १५ मिनिटांतील परवानगी वापर"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"मागील एका मिनिटातील परवानगी वापर"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"मागील २४ तासांमध्ये न वापरलेली"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"मागील सात दिवसांमध्ये न वापरलेली"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{मागील # दिवसामध्ये न वापरलेली}other{मागील # दिवसांमध्ये न वापरलेली}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{मागील # तासामध्ये न वापरलेली}other{मागील # तासांमध्ये न वापरलेली}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{एका अॅपने वापरल्या}other{# अॅप्सनी वापरल्या}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"डॅशबोर्डमध्ये सर्व पहा"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"यानुसार फिल्टर केले: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"फक्त मीडिया ॲक्सेस करण्यासाठी अनुमती द्या"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"सर्व वेळी अनुमती द्या"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"फक्त अॅप वापरत असताना अनुमती द्या"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"सर्व फोटोना अनुमती द्या"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"निवडलेल्या फोटोना अनुमती द्या"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक वेळी विचारा"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमती देऊ नका"</string> <string name="precise_image_description" msgid="6349638632303619872">"अचूक स्थान"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"अनुमती नाही"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"सर्व फाइलचा ॲक्सेस असलेली आणखी ॲप्स पहा"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{एक दिवस}other{# दिवस}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{एक तास}other{# तास}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{एक मिनिट}other{# मिनिटे}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{एक सेकंद}other{# सेकंद}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# तास}other{# तास}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# मिनिट}other{# मिनिटे}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# सेकंद}other{# सेकंद}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"परवानगी रिमाइंडर"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"एक न वापरलेले ॲप"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> न वापरलेली अॅप्स"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसवरील <b>फोटो, व्हिडिओ, संगीत, ऑडिओ व इतर फाइल</b> अॅक्सेस करू द्यायच्या?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसवरील संगीत आणि ऑडिओ अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसवरील फोटो आणि व्हिडिओ अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला आणखी फोटो अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला ऑडिओ रेकॉर्ड करू द्यायचा?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ॲप फक्त तुम्ही ॲप वापरत असतानाच ऑडिओ रेकॉर्ड करू शकते"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला ऑडिओ रेकॉर्ड करायची अनुमती द्यायची?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"तुम्ही कॉपी केलेला मजकूर, इमेज किंवा इतर आशय ॲप्स अॅक्सेस करतात तेव्हा, मेसेज दाखवा"</string> <string name="show_password_title" msgid="2877269286984684659">"पासवर्ड दाखवा"</string> <string name="show_password_summary" msgid="1110166488865981610">"तुम्ही टाइप कराल त्याप्रमाणे वर्ण थोडक्यात डिस्प्ले करा"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"हे अॅप तृतीय पक्षांसोबत <xliff:g id="PERMISSION_NAME">%s</xliff:g> डेटा शेअर करू शकते असे या अॅपने नमूद केले आहे"</string> </resources> diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml index 71df0dd2d..675a595b4 100644 --- a/PermissionController/res/values-ms/strings.xml +++ b/PermissionController/res/values-ms/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Kekalkan “Semasa apl sedang digunakan”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Simpan “Kali ini sahaja”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lagi maklumat"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Benarkan akses kepada semua foto"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pilih foto"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pilih lagi foto"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Jangan pilih lebih banyak foto"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Jangan benarkan juga"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Tolak"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> daripada <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 hari}other{# hari}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 jam}other{# jam}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 saat}other{# saat}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# hari}other{# hari}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# jam}other{# jam}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# saat}other{# saat}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Sebarang kebenaran"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Pada bila-bila masa"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 hari yang lalu"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 jam yang lalu"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Sejam yang lalu"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 minit yang lalu"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 minit terakhir"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# hari yang lalu}other{# hari yang lalu}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# jam yang lalu}other{# jam yang lalu}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# minit yang lalu}other{# minit yang lalu}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Tiada penggunaan kebenaran"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Akses terbaharu pada bila-bila masa"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Akses terbaharu dalam 7 hari terakhir"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Penggunaan kebenaran dalam 1 jam terakhir"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Penggunaan kebenaran dalam 15 minit terakhir"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Penggunaan kebenaran dalam 1 minit terakhir"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Tidak digunakan dalam tempoh 24 jam yang lalu"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Tidak digunakan dalam tempoh 7 hari yang lalu"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Tidak digunakan dalam # hari yang lalu}other{Tidak digunakan dalam # hari yang lalu}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Tidak digunakan dalam # jam yang lalu}other{Tidak digunakan dalam # jam yang lalu}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Digunakan oleh 1 apl}other{Digunakan oleh # apl}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Lihat semua dalam Papan Pemuka"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Ditapis mengikut: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Benarkan akses kepada media sahaja"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Benarkan sepanjang masa"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Benarkan hanya semasa menggunakan apl"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Benarkan semua foto"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Benarkan foto dipilih"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Tanya setiap kali"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan benarkan"</string> <string name="precise_image_description" msgid="6349638632303619872">"Lokasi tepat"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Tidak dibenarkan"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Lihat lagi apl yang boleh mengakses semua fail"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 hari}other{# hari}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 jam}other{# jam}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minit}other{# minit}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 saat}other{# saat}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# jam}other{# jam}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minit}other{# minit}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# saat}other{# saat}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Peringatan kebenaran"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 apl tidak digunakan"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apl yang tidak digunakan"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses <b>foto, video, muzik, audio dan fail lain</b> pada peranti ini?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses muzik dan audio pada peranti ini?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses foto dan video pada peranti ini?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Beri <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> akses kepada lebih banyak foto?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> merakam audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Apl hanya boleh merakam audio semasa anda menggunakan apl tersebut"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> merakam audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Tunjukkan mesej apabila apl mengakses teks, imej atau kandungan lain yang telah anda salin"</string> <string name="show_password_title" msgid="2877269286984684659">"Tunjukkan kata laluan"</string> <string name="show_password_summary" msgid="1110166488865981610">"Paparkan aksara seketika sambil anda menaip"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Apl ini menyatakan bahawa data <xliff:g id="PERMISSION_NAME">%s</xliff:g> mungkin dikongsikan dengan pihak ketiga"</string> </resources> diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml index 253af91d1..61d5b6251 100644 --- a/PermissionController/res/values-my/strings.xml +++ b/PermissionController/res/values-my/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"အက်ပ်ကို အသုံးပြုနေစဉ်\" ကို ဆက်ထားရန်"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"ဤတစ်ကြိမ်သာ\" ကို မှတ်ထားရန်"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"နောက်ထပ်"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"မည်သို့ပင်ဖြစ်စေ ခွင့်မပြုပါ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ပယ်ရန်"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ထဲမှ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ရက်}other{# ရက်}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 နာရီ}other{# နာရီ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{၁ မိနစ်}other{# မိနစ်}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{၁ စက္ကန့်}other{# စက္ကန့်}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ရက်}other{# ရက်}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# နာရီ}other{# နာရီ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# မိနစ်}other{# မိနစ်}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# စက္ကန့်}other{# စက္ကန့်}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"မည်သည့် ခွင့်ပြုချက်မဆို"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"အချိန်မရွေး"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ပြီးခဲ့သော ၇ ရက်"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ပြီးခဲ့သော ၂၄ နာရီ"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"ပြီးခဲ့သော ၁ နာရီ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ပြီးခဲ့သော ၁၅ မိနစ်"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ပြီးခဲ့သည့် ၁ မိနစ်"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ပြီးခဲ့သော # ရက်}other{ပြီးခဲ့သော # ရက်}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{ပြီးခဲ့သော # နာရီ}other{ပြီးခဲ့သော # နာရီ}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ပြီးခဲ့သော # မိနစ်}other{ပြီးခဲ့သော # မိနစ်}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"မည်သည့်ခွင့်ပြုချက်မှ မသုံးပါ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"အချိန်မရွေး လတ်တလောအဖြစ်ဆုံး ဝင်သုံးမှု"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ပြီးခဲ့သော ၇ ရက်အတွင်း လတ်တလော ဝင်သုံးမှု"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ပြီးခဲ့သော ၁ မိနစ်အတွင်း ခွင့်ပြုချက်သုံးစွဲမှု"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ပြီးခဲ့သော ၁၅ မိနစ်အတွင်း ခွင့်ပြုချက်သုံးစွဲမှု"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ပြီးခဲ့သော ၁ မိနစ်အတွင်း ခွင့်ပြုချက်သုံးစွဲမှု"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ပြီးခဲ့သည့် ၂၄ နာရီအတွင်း အသုံးမပြုပါ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ပြီးခဲ့သည့် ၇ ရက်အတွင်း အသုံးမပြုပါ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ပြီးခဲ့သော # ရက်အတွင်း သုံးမထားပါ}other{ပြီးခဲ့သော # ရက်အတွင်း သုံးမထားပါ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ပြီးခဲ့သော # နာရီအတွင်း သုံးမထားပါ}other{ပြီးခဲ့သော # နာရီအတွင်း သုံးမထားပါ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{အက်ပ် ၁ ခုက အသုံးပြုထားသည်}other{အက်ပ် # ခုက အသုံးပြုထားသည်}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"အားလုံးကို \'ဒက်ရှ်ဘုတ်\' တွင်ကြည့်ရန်"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"စစ်ထုတ်စနစ်- <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"မီဒီယာကိုသာ သုံးခွင့်ပြုရန်"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"အမြဲ ခွင့်ပြုရန်"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"အက်ပ်ကိုသုံးစဉ်သာ ခွင့်ပြုရန်"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"အမြဲမေးရန်"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ခွင့်မပြုပါ"</string> <string name="precise_image_description" msgid="6349638632303619872">"နေရာအတိအကျ"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"ခွင့်ပြုမထားပါ"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ဖိုင်အားလုံးသုံးနိုင်သည့် နောက်ထပ်အက်ပ်များ ကြည့်ရန်"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ရက်}other{# ရက်}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 နာရီ}other{# နာရီ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{၁ မိနစ်}other{# မိနစ်}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{၁ စက္ကန့်}other{# စက္ကန့်}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# နာရီ}other{# နာရီ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# မိနစ်}other{# မိနစ်}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# စက္ကန့်}other{# စက္ကန့်}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ခွင့်ပြုချက် သတိပေးမှုများ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"အသုံးမပြုသောအက်ပ် 1 ခု"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"အသုံးမပြုသောအက်ပ် <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ခု"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"စက်ရှိ <b>ဓာတ်ပုံ၊ ဗီဒီယို၊ တေးဂီတ၊ အသံနှင့်အခြားဖိုင်များ</b> ကို <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သုံးခွင့်ပေးမလား။"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ဤစက်ရှိ တေးဂီတနှင့် အသံဖိုင်ကို <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သုံးခွင့်ပေးမလား။"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ဤစက်ရှိ ဓာတ်ပုံနှင့် ဗီဒီယိုများကို <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သုံးခွင့်ပေးမလား။"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ကို အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ဤအက်ပ်ကို အသုံးပြုနေသည့် အချိန်တွင်သာ ၎င်းက အသံဖမ်းနိုင်သည်။"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ကို အသံဖမ်းခွင့် ပေးလိုပါသလား။"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"အက်ပ်များက သင်မိတ္တူကူးထားသော စာသား၊ ပုံများ (သို့) အခြားအကြောင်းအရာကို သုံးသောအခါ အကြောင်းကြားပါ"</string> <string name="show_password_title" msgid="2877269286984684659">"စကားဝှက်များပြရန်"</string> <string name="show_password_summary" msgid="1110166488865981610">"စာရိုက်သည့်အခါ အက္ခရာများကို ခဏတာပြသည်"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml index cd1123900..ff8a2f9b1 100644 --- a/PermissionController/res/values-nb/strings.xml +++ b/PermissionController/res/values-nb/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behold «Mens appen er i bruk»"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behold «Bare denne gangen»"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Gi tilgang til alle bilder"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Velg bilder"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Velg flere bilder"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ikke velg flere bilder"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ikke tillat likevel"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Avvis"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dag}other{# dager}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 time}other{# timer}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek}other{# sek}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dag}other{# dager}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# time}other{# timer}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek}other{# sek}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Hvilken som helst tillatelse"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Når som helst"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"De siste 7 dagene"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"De siste 24 timene"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Den siste timen"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"De siste 15 minuttene"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Det siste minuttet"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Den siste dagen}other{De siste # dagene}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Den siste timen}other{De siste # timene}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Det siste minuttet}other{De siste # minuttene}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ingen bruk av tillatelsen"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Siste tilgang når som helst"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Siste tilgang i løpet av de siste syv dagene"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Bruk av tillatelser den siste timen"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Bruk av tillatelser de siste 15 minuttene"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Bruk av tillatelser det siste minuttet"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ikke brukt de siste 24 timene"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Ikke brukt i løpet av de siste 7 dagene"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Ikke brukt i løpet av den siste dagen}other{Ikke brukt i løpet av de siste # dagene}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Ikke brukt den siste timen}other{Ikke brukt de siste # timene}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Brukt av 1 app}other{Brukt av # apper}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Se alt i oversikten"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrert etter: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Har bare tilgang til medier"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillat hele tiden"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Bare tillat når appen brukes"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillat alle bilder"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillat valgte bilder"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Spør hver gang"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ikke tillat"</string> <string name="precise_image_description" msgid="6349638632303619872">"Nøyaktig posisjon"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ikke tillatt"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se flere apper som kan bruke alle filer"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dager}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 time}other{# timer}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minutt}other{# minutter}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekund}other{# sekunder}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# time}other{# timer}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minutt}other{# minutter}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekund}other{# sekunder}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Påminnelser om tillatelser"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Én ubrukt app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ubrukte apper"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> bruke <b>bilder, videoer, musikk, lyd og andre filer</b> på denne enheten?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> bruke musikk og lyd på denne enheten?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> bruke bilder og videoer på denne enheten?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vil du gi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilgang til flere bilder?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ta opp lyd?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bare ta opp lyd mens du bruker den."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ta opp lyd?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vis en melding når apper bruker tekst, bilder eller annet innhold du har kopiert"</string> <string name="show_password_title" msgid="2877269286984684659">"Vis passord"</string> <string name="show_password_summary" msgid="1110166488865981610">"Vis tegnene et øyeblikk mens du skriver"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Denne appen har oppgitt at den kan dele <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredjeparter"</string> </resources> diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml index a3070787a..45aab2d35 100644 --- a/PermissionController/res/values-ne/strings.xml +++ b/PermissionController/res/values-ne/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“एप प्रयोगमा भएको बेलामा” शीर्षक कायम राख्नुहोस्"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“यस बेला मात्र” राख्नुहोस्"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"थप जानकारी"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"जे भए पनि फेरि नसोध्नुहोस्"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"खारेज गर्नुहोस्"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> मध्ये <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{१ दिन}other{# दिन}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{१ घण्टा}other{# घण्टा}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{१ मिनेट}other{# मिनेट}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{१ सेकेन्ड}other{# सेकेन्ड}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# दिन}other{# दिन}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# घण्टा}other{# घण्टा}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# मिनेट}other{# मिनेट}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# सेकेन्ड}other{# सेकेन्ड}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"कुनै पनि अनुमति"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"कुनै पनि समय"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"पछिल्ला ७ दिन"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"पछिल्लो २४ घन्टा"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"पछिल्लो १ घन्टा"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"पछिल्लो १५ मिनेट"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"पछिल्लो १ मिनेट"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{पछिल्लो # दिन}other{पछिल्ला # दिन}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{पछिल्लो # घण्टा}other{पछिल्ला # घण्टा}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{पछिल्लो # मिनेट}other{पछिल्ला # मिनेट}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"उपयोगको अनुमति छैन"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"पछिल्लो पटक जुनसुकै बेला गरिएको पहुँच"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"गत ७ दिनमा पछिल्लो पटक गरिएको पहुँच"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"पछिल्लो १ घन्टामा गरिएको अनुमतिको प्रयोग"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"पछिल्लो १५ मिनेटमा गरिएको अनुमतिको प्रयोग"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"पछिल्लो १ मिनेटमा गरिएको अनुमतिको प्रयोग"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"पछिल्ला २४ घण्टामा प्रयोग गरिएको छैन"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"पछिल्ला ७ दिनमा प्रयोग गरिएको छैन"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{पछिल्लो # दिनमा प्रयोग गरिएको छैन}other{पछिल्ला # दिनमा प्रयोग गरिएको छैन}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{पछिल्लो # घण्टामा प्रयोग गरिएको छैन}other{पछिल्ला # घण्टामा प्रयोग गरिएको छैन}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{एउटा एपले प्रयोग गरेको}other{# वटा एपले प्रयोग गरेको}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ड्यासबोर्डमा सबै कुरा हेर्नुहोस्"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"यसअनुसार फिल्टर गरिएको: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"केवल मिडिया प्रयोग गर्ने अनुमति दिइयोस्"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"सधैँ अनुमति दिइयोस्"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"यो एप प्रयोग गरिरहेका बेला मात्र अनुमति दिइयोस्"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक पटक सोधियोस्"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति नदिइयोस्"</string> <string name="precise_image_description" msgid="6349638632303619872">"सटीक स्थान"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"अनुमति नदिइएका"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"सबै फाइल हेर्ने र प्रयोग गर्ने अनुमति भएका थप एपहरू हेर्नुहोस्"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{१ दिन}other{# दिन}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{१ घण्टा}other{# घण्टा}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{१ मिनेट}other{# मिनेट}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{१ सेकेन्ड}other{# सेकेन्ड}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# घण्टा}other{# घण्टा}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# मिनेट}other{# मिनेट}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# सेकेन्ड}other{# सेकेन्ड}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"अनुमतिसम्बन्धी रिमाइन्डरहरू"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"प्रयोग नगरिएको १ एप"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"प्रयोग नगरिएका <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> एपहरू"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई यो डिभाइसमा रहेका <b>फोटो, भिडियो, सङ्गीत, अडियो तथा अन्य फाइलहरू</b> प्रयोग गर्न दिने हो?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई यस डिभाइसमा रहेका सङ्गीत तथा अन्य अडियो फाइलहरू प्रयोग गर्न दिने हो?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई यस डिभाइसमा रहेका फोटो र भिडियोहरू प्रयोग गर्न दिने हो?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई अडियो रेकर्ड गर्न दिने हो?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"तपाईंले यो एप प्रयोग गरिरहेका बेलामा मात्र यसले अडियो रेकर्ड गर्न सक्ने छ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई अडियो रेकर्ड गर्न दिने हो?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"मैले कपी गरेका टेक्स्ट, फोटो वा अन्य सामग्री एपहरूले प्रयोग गर्दा म्यासेज देखाइयोस्"</string> <string name="show_password_title" msgid="2877269286984684659">"पासवर्डहरू देखाइयोस्"</string> <string name="show_password_summary" msgid="1110166488865981610">"टाइप गर्दै गर्दा वर्णहरू झलक्क देखाइयोस्"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-night/themes.xml b/PermissionController/res/values-night/themes.xml index b4257ab4e..7ac9b190b 100644 --- a/PermissionController/res/values-night/themes.xml +++ b/PermissionController/res/values-night/themes.xml @@ -40,5 +40,5 @@ <item name="android:background">@color/divider_color_secondary</item> </style> - <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.Alert" /> + <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar" /> </resources> diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml index 2ec869034..70d7b96ac 100644 --- a/PermissionController/res/values-nl/strings.xml +++ b/PermissionController/res/values-nl/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Instelling \'Terwijl de app wordt gebruikt\' behouden"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\'Alleen deze keer\' behouden"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer informatie"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Toegang geven tot alle foto\'s"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Foto\'s selecteren"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Meer foto\'s selecteren"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Geen foto\'s meer selecteren"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Toch niet toestaan"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Sluiten"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dag}other{# dagen}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 uur}other{# uur}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sec}other{# sec}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dag}other{# dagen}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# uur}other{# uur}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Minstens 1 recht"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Altijd"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Afgelopen 7 dagen"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Afgelopen 24 uur"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Afgelopen uur"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Afgelopen 15 minuten"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Afgelopen minuut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Afgelopen dag}other{Afgelopen # dagen}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Afgelopen uur}other{Afgelopen # uur}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Afgelopen minuut}other{Afgelopen # minuten}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Geen rechtengebruik"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Laatste toegang"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Laatste toegang in de afgelopen zeven dagen"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Rechtengebruik in het afgelopen uur"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Rechtengebruik in de afgelopen 15 minuten"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Rechtengebruik in de afgelopen minuut"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Niet gebruikt in de afgelopen 24 uur"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Niet gebruikt in de afgelopen 7 dagen"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Niet gebruikt in de afgelopen dag}other{Niet gebruikt in de afgelopen # dagen}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Niet gebruikt in het afgelopen uur}other{Niet gebruikt in de afgelopen # uur}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Gebruikt door 1 app}other{Gebruikt door # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Alles op dashboard tonen"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Gefilterd op: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Alleen toegang tot media toestaan"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Altijd toestaan"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Toestaan bij gebruik van app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Alle foto\'s toestaan"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Geselecteerde foto\'s toestaan"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Altijd vragen"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Niet toestaan"</string> <string name="precise_image_description" msgid="6349638632303619872">"Exacte locatie"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Niet toegestaan"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bekijk meer apps die toegang tot alle bestanden hebben."</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dagen}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 uur}other{# uur}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuut}other{# minuten}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 seconde}other{# seconden}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# uur}other{# uur}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuut}other{# minuten}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# seconde}other{# seconden}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Herinneringen voor rechten"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 niet-gebruikte app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> niet-gebruikte apps"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot <b>foto\'s, video\'s, muziek, audio en andere bestanden</b> op dit apparaat?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot muziek en audio op dit apparaat?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot foto\'s en video\'s op dit apparaat?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot meer foto\'s?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestaan om audio op te nemen?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Deze app kan alleen audio opnemen als je de app gebruikt"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestaan om audio op te nemen?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Toon een bericht als apps toegang hebben tot tekst, afbeeldingen of andere content die je hebt gekopieerd"</string> <string name="show_password_title" msgid="2877269286984684659">"Wachtwoorden tonen"</string> <string name="show_password_summary" msgid="1110166488865981610">"Tekens kort tonen terwijl je typt"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Deze app heeft vermeld dat deze <xliff:g id="PERMISSION_NAME">%s</xliff:g>-gegevens kan delen met derden."</string> </resources> diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml index 0eda28e08..1a3696036 100644 --- a/PermissionController/res/values-or/strings.xml +++ b/PermissionController/res/values-or/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ଆପଟି ବ୍ୟବହାରରେ ଥିବା ସମୟରେ”କୁ ରଖନ୍ତୁ"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“କେବଳ ଏହି ସମୟ” ରଖନ୍ତୁ"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ଅଧିକ ସୂଚନା"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ସମସ୍ତ ଫଟୋକୁ ଆକ୍ସେସ କରିବାର ଅନୁମତି ଦିଅନ୍ତୁ"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ଫଟୋଗୁଡ଼ିକୁ ଚୟନ କରନ୍ତୁ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ଅଧିକ ଫଟୋ ଚୟନ କରନ୍ତୁ"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ଅଧିକ ଫଟୋ ଚୟନ କରନ୍ତୁ ନାହିଁ"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ଯେ କୌଣସି ମତେ ଅନୁମତି ଦିଅ ନାହିଁ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ଖାରଜ କରନ୍ତୁ"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ରୁ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ଦିନ}other{# ଦିନ}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ମିନିଟ}other{# ମିନିଟ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 ସେକେଣ୍ଡ}other{# ସେକେଣ୍ଡ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ଦିନ}other{# ଦିନ}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# ମିନିଟ}other{# ମିନିଟ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ସେକେଣ୍ଡ}other{# ସେକେଣ୍ଡ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ଯେକୌଣସି ଅନୁମତି"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ଯେକୌଣସି ସମୟରେ"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ଗତ 7 ଦିନ"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ଗତ 24 ଘଣ୍ଟା"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"ଗତ 1 ଘଣ୍ଟା"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ଗତ 15 ମିନିଟ୍"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ଗତ 1 ମିନିଟ୍ରେ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ଗତ # ଦିନ}other{ଗତ # ଦିନ}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{ଗତ # ଘଣ୍ଟା}other{ଗତ # ଘଣ୍ଟା}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ଗତ # ମିନିଟ}other{ଗତ # ମିନିଟ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ବ୍ୟବହାର ପାଇଁ କୌଣସି ଅନୁମତି ନାହିଁ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ଯେକୌଣସି ସମୟରେ କରାଯାଇଥିବା ସମ୍ପ୍ରତ୍ତି ଆକ୍ସେସ୍"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ଗତ 7 ଦିନରେ ଅତି ସମ୍ପ୍ରତ୍ତି ହୋଇଥିବା ଆକ୍ସେସ୍"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ଗତ 1 ଘଣ୍ଟାରେ ବ୍ୟବହୃତ ଅନୁମତି"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ଗତ 15 ମିନିଟ୍ରେ ବ୍ୟବହୃତ ହୋଇଥିବା ଅନୁମତି"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ଗତ 1 ମିନିଟ୍ରେ ବ୍ୟବହୃତ ହୋଇଥିବା ଅନୁମତି"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ଗତ 24 ଘଣ୍ଟାରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ଗତ 7 ଦିନରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ଗତ # ଦିନରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ}other{ଗତ # ଦିନରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ଗତ # ଘଣ୍ଟାରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ}other{ଗତ # ଘଣ୍ଟାରେ ବ୍ୟବହାର କରାଯାଇନାହିଁ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1ଟି ଆପ ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଇଛି}other{#ଟି ଆପ ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଇଛି}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ଡ୍ୟାସ୍ବୋର୍ଡରେ ସବୁ ଦେଖନ୍ତୁ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"<xliff:g id="PERM">%1$s</xliff:g> ଦ୍ବାରା ଫିଲ୍ଟର୍ କରାଯାଇଛି"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"କେବଳ ମିଡିଆକୁ ଆକ୍ସେସ୍ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ସବୁ ସମୟ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ଆପ୍ ବ୍ୟବହାର ବେଳେ କେବଳ ଅନୁମତି ଦିଅନ୍ତୁ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ସମସ୍ତ ଫଟୋକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ଚୟନିତ ଫଟୋଗୁଡ଼ିକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string> <string name="precise_image_description" msgid="6349638632303619872">"ସଠିକ୍ ଲୋକେସନ୍"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ଅନୁମତି ନାହିଁ"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ସମସ୍ତ ଫାଇଲକୁ ଆକ୍ସେସ କରିପାରୁଥିବା ଅଧିକ ଆପ୍ସ ଦେଖନ୍ତୁ"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ଦିନ}other{# ଦିନ}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 ମିନିଟ}other{# ମିନିଟ}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 ସେକେଣ୍ଡ}other{# ସେକେଣ୍ଡ}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# ମିନିଟ}other{# ମିନିଟ}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ସେକେଣ୍ଡ}other{# ସେକେଣ୍ଡ}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ଅନୁମତି ରିମାଇଣ୍ଡର୍"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1ଟି ଅବ୍ୟବହୃତ ଆପ୍"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g>ଟି ଅବ୍ୟବହୃତ ଆପ୍"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ଏହି ଡିଭାଇସରେ ଥିବା<b>ଫଟୋ, ଭିଡିଓ, ମ୍ୟୁଜିକ, ଅଡିଓ ଓ ଅନ୍ୟ ଫାଇଲ</b> ଆକ୍ସେସ କରିବା ପାଇଁ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ଏହି ଡିଭାଇସରେ ଥିବା ମ୍ୟୁଜିକ ଏବଂ ଅଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ଏହି ଡିଭାଇସରେ ଥିବା ଫଟୋ ଏବଂ ଭିଡିଓଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅନୁମତି ଦେବେ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ଅଧିକ ଫଟୋକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ଆପଣ ଆପକୁ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ କେବଳ ଏହା ଅଡିଓ ରେକର୍ଡ କରିବାକୁ ସକ୍ଷମ ହେବ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଅଡିଓ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦେବେ କି?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ଯେତେବେଳେ ଆପଣ କପି କରିଥିବା ଟେକ୍ସଟ, ଇମେଜ କିମ୍ବା ଅନ୍ୟ ବିଷୟବସ୍ତୁକୁ ଆପ୍ସ ଆକ୍ସେସ କରେ, ସେତେବେଳେ ଏକ ମେସେଜ ଦେଖାନ୍ତୁ"</string> <string name="show_password_title" msgid="2877269286984684659">"ପାସୱାର୍ଡଗୁଡ଼ିକ ଦେଖାନ୍ତୁ"</string> <string name="show_password_summary" msgid="1110166488865981610">"ଆପଣ ଟାଇପ କରିବା ସମୟରେ କେରେକ୍ଟରଗୁଡ଼ିକୁ କିଛି ସମୟ ପାଇଁ ଡିସପ୍ଲେ କରନ୍ତୁ"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ଏହି ଆପ ଉଲ୍ଲେଖ କରିଛି ଯେ ଏହା ତୃତୀୟ ପକ୍ଷଗୁଡ଼ିକ ସହ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ଡାଟା ସେୟାର କରିପାରେ"</string> </resources> diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml index 60318c9a2..c255f1205 100644 --- a/PermissionController/res/values-pa/strings.xml +++ b/PermissionController/res/values-pa/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ” ਨੂੰ ਰੱਖੋ"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ਸਿਰਫ਼ ਇਸ ਸਮੇਂ ਲਈ ਇਜਾਜ਼ਤ ਦਿਓ” ਰੱਖੋ"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ਹੋਰ ਜਾਣਕਾਰੀ"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ਸਾਰੀਆਂ ਫ਼ੋਟੋਆਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ਫ਼ੋਟੋਆਂ ਚੁਣੋ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ਹੋਰ ਫ਼ੋਟੋਆਂ ਚੁਣੋ"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ਹੋਰ ਫ਼ੋਟੋਆਂ ਨਾ ਚੁਣੋ"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ਫਿਰ ਵੀ ਆਗਿਆ ਨਾ ਦਿਓ"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ਖਾਰਜ ਕਰੋ"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ਵਿੱਚੋਂ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ਦਿਨ}one{# ਦਿਨ}other{# ਦਿਨ}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ਘੰਟਾ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 ਮਿੰਟ}one{# ਮਿੰਟ}other{# ਮਿੰਟ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 ਸਕਿੰਟ}one{# ਸਕਿੰਟ}other{# ਸਕਿੰਟ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ਦਿਨ}one{# ਦਿਨ}other{# ਦਿਨ}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ਘੰਟਾ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# ਮਿੰਟ}one{# ਮਿੰਟ}other{# ਮਿੰਟ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# ਸਕਿੰਟ}one{# ਸਕਿੰਟ}other{# ਸਕਿੰਟ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ਕੋਈ ਵੀ ਇਜਾਜ਼ਤ"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ਕਿਸੇ ਵੀ ਵੇਲੇ"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"ਪਿਛਲੇ 7 ਦਿਨ"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"ਪਿਛਲੇ 24 ਘੰਟੇ"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"ਪਿਛਲਾ 1 ਘੰਟਾ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"ਪਿਛਲੇ 15 ਮਿੰਟ"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"ਪਿਛਲੇ 1 ਮਿੰਟ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{ਪਿਛਲਾ # ਦਿਨ}one{ਪਿਛਲਾ # ਦਿਨ}other{ਪਿਛਲੇ # ਦਿਨ}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{ਪਿਛਲਾ # ਘੰਟਾ}one{ਪਿਛਲਾ # ਘੰਟਾ}other{ਪਿਛਲੇ # ਘੰਟੇ}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{ਪਿਛਲਾ # ਮਿੰਟ}one{ਪਿਛਲਾ # ਮਿੰਟ}other{ਪਿਛਲੇ # ਮਿੰਟ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ਕੋਈ ਇਜਾਜ਼ਤ ਨਹੀਂ ਵਰਤੀ ਗਈ"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ਕਿਸੇ ਵੀ ਵੇਲੇ ਦੀ ਸਭ ਤੋਂ ਹਾਲੀਆ ਪਹੁੰਚ"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"ਪਿਛਲੇ 7 ਦਿਨਾਂ ਵਿੱਚ ਸਭ ਤੋਂ ਹਾਲੀਆ ਪਹੁੰਚ"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"ਪਿਛਲੇ 1 ਘੰਟੇ ਵਿੱਚ ਇਜਾਜ਼ਤ ਵਰਤੋਂ"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"ਪਿਛਲੇ 15 ਮਿੰਟ ਵਿੱਚ ਇਜਾਜ਼ਤ ਵਰਤੋਂ"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"ਪਿਛਲੇ 1 ਮਿੰਟ ਵਿੱਚ ਇਜਾਜ਼ਤ ਵਰਤੋਂ"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ਪਿਛਲੇ 24 ਘੰਟਿਆਂ ਵਿੱਚ ਵਰਤੀਆਂ ਨਹੀਂ ਗਈਆਂ"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ਪਿਛਲੇ 7 ਦਿਨਾਂ ਵਿੱਚ ਵਰਤੀਆਂ ਨਹੀਂ ਗਈਆਂ"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ਪਿਛਲੇ # ਦਿਨ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}one{ਪਿਛਲੇ # ਦਿਨ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}other{ਪਿਛਲੇ # ਦਿਨਾਂ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ਪਿਛਲੇ # ਘੰਟੇ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}one{ਪਿਛਲੇ # ਘੰਟੇ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}other{ਪਿਛਲੇ # ਘੰਟਿਆਂ ਵਿੱਚ ਨਹੀਂ ਵਰਤੀ ਗਈ}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}one{# ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}other{# ਐਪਾਂ ਵੱਲੋਂ ਇਜਾਜ਼ਤਾਂ ਵਰਤੀਆਂ ਗਈਆਂ}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ਸਭ ਡੈਸ਼ਬੋਰਡ ਵਿੱਚ ਦੇਖੋ"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"ਇਸ ਮੁਤਾਬਕ ਫਿਲਟਰ ਕੀਤਾ ਗਿਆ: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ਸਿਰਫ਼ ਮੀਡੀਆ ਲਈ ਪਹੁੰਚ ਕਰਨ ਦਿਓ"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ਹਰ ਵੇਲੇ ਕਰਨ ਦਿਓ"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ਸਿਰਫ਼ ਐਪ ਵਰਤੇ ਜਾਣ ਵੇਲੇ ਕਰਨ ਦਿਓ"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ਸਾਰੀਆਂ ਫ਼ੋਟੋਆਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ਚੋਣਵੀਆਂ ਫ਼ੋਟੋਆਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ਨਾ ਕਰਨ ਦਿਓ"</string> <string name="precise_image_description" msgid="6349638632303619872">"ਸਹੀ ਟਿਕਾਣਾ"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ਗੈਰ-ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਣ ਵਾਲੀਆਂ ਹੋਰ ਐਪਾਂ ਦੇਖੋ"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ਦਿਨ}one{# ਦਿਨ}other{# ਦਿਨ}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ਘੰਟਾ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 ਮਿੰਟ}one{# ਮਿੰਟ}other{# ਮਿੰਟ}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 ਸਕਿੰਟ}one{# ਸਕਿੰਟ}other{# ਸਕਿੰਟ}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ਘੰਟਾ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# ਮਿੰਟ}one{# ਮਿੰਟ}other{# ਮਿੰਟ}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# ਸਕਿੰਟ}one{# ਸਕਿੰਟ}other{# ਸਕਿੰਟ}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"ਇਜਾਜ਼ਤਾਂ ਦੀਆਂ ਯਾਦ-ਸੂਚਨਾਵਾਂ"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ਅਣਵਰਤੀ ਐਪ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ਅਣਵਰਤੀਆਂ ਐਪਾਂ"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ <b>ਫ਼ੋਟੋਆਂ, ਵੀਡੀਓ, ਸੰਗੀਤ, ਆਡੀਓ ਅਤੇ ਹੋਰ ਫ਼ਾਈਲਾਂ</b> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸੰਗੀਤ ਅਤੇ ਆਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਹੋਰ ਫ਼ੋਟੋਆਂ ਤੱਕ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੇਣੀ ਹੈ?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਹੀ ਐਪ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕੇਗੀ"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ਜਦੋਂ ਐਪਾਂ ਲਿਖਤ, ਚਿੱਤਰ ਜਾਂ ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਹੋਰ ਸਮੱਗਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਦੀਆਂ ਹਨ, ਤਾਂ ਕੋਈ ਸੁਨੇਹਾ ਦਿਖਾਓ"</string> <string name="show_password_title" msgid="2877269286984684659">"ਪਾਸਵਰਡ ਦਿਖਾਓ"</string> <string name="show_password_summary" msgid="1110166488865981610">"ਟਾਈਪ ਕਰਨ ਵੇਲੇ ਅੱਖਰ-ਚਿੰਨ੍ਹਾਂ ਨੂੰ ਥੋੜ੍ਹੇ ਸਮੇਂ ਲਈ ਦਿਖਾਓ"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ਇਸ ਐਪ ਨੇ ਸਪਸ਼ਟ ਕੀਤਾ ਕਿ ਇਹ ਤੀਜੀਆਂ ਧਿਰਾਂ ਨਾਲ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ਡਾਟੇ ਨੂੰ ਸਾਂਝਾ ਕਰ ਸਕਦੀ ਹੈ"</string> </resources> diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml index 07a85de06..52f14e16d 100644 --- a/PermissionController/res/values-pl/strings.xml +++ b/PermissionController/res/values-pl/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zachowaj „Podczas używania aplikacji”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zachowaj „Tylko tym razem”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Więcej"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Zezwól na dostęp do wszystkich zdjęć"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Wybierz zdjęcia"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Wybierz więcej zdjęć"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nie wybieraj kolejnych zdjęć"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"I tak nie zezwalaj"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odrzuć"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dzień}few{# dni}many{# dni}other{# dnia}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 godzina}few{# godziny}many{# godzin}other{# godziny}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}few{# min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}few{# s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dzień}few{# dni}many{# dni}other{# dnia}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# godzina}few{# godziny}many{# godzin}other{# godziny}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}few{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}few{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Dowolne uprawnienie"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Dowolny czas"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Ostatnie 7 dni"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Ostatnie 24 godziny"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Ostatnia godzina"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Ostatnie 15 minut"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Ostatnia minuta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ostatni dzień}few{Ostatnie # dni}many{Ostatnie # dni}other{Ostatnie # dnia}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ostatnia godzina}few{Ostatnie # godz.}many{Ostatnie # godz.}other{Ostatnie # godz.}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ostatnia minuta}few{Ostatnie # minuty}many{Ostatnie # minut}other{Ostatnie # minuty}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Brak użycia uprawnień"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Ostatni dostęp w dowolnym okresie"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Najnowsze użycie w ciągu ostatnich 7 dni"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Użycie uprawnień w ciągu ostatniej godziny"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Użycie uprawnień w ciągu ostatnich 15 minut"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Użycie uprawnień w ciągu ostatniej minuty"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nie używano w ciągu ostatnich 24 godzin"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nie używano w ciągu ostatnich 7 dni"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nie używano w ciągu ostatniego dnia}few{Nie używano w ciągu ostatnich # dni}many{Nie używano w ciągu ostatnich # dni}other{Nie używano w ciągu ostatniej # dnia}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nie używano w ciągu ostatniej godziny}few{Nie używano w ciągu ostatnich # godzin}many{Nie używano w ciągu ostatnich # godzin}other{Nie używano w ciągu ostatnich # godzin}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Uprawnienie używane przez 1 aplikację}few{Uprawnienie używane przez # aplikacje}many{Uprawnienie używane przez # aplikacji}other{Uprawnienie używane przez # aplikacji}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Pokaż wszystko w panelu"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrowane według: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Pozwól na dostęp tylko do multimediów"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Zawsze zezwalaj"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Zezwalaj tylko podczas używania aplikacji"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Zezwól na wszystkie zdjęcia"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Zezwól na wybrane zdjęcia"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Zawsze pytaj"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nie zezwalaj"</string> <string name="precise_image_description" msgid="6349638632303619872">"Dokładna lokalizacja"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nie mają dostępu"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobacz więcej aplikacji z dostępem do wszystkich plików"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dzień}few{# dni}many{# dni}other{# dnia}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 godzina}few{# godziny}many{# godzin}other{# godziny}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuta}few{# minuty}many{# minut}other{# minuty}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunda}few{# sekundy}many{# sekund}other{# sekundy}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# godzina}few{# godziny}many{# godzin}other{# godziny}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuta}few{# minuty}many{# minut}other{# minuty}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunda}few{# sekundy}many{# sekund}other{# sekundy}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Przypomnienia o uprawnieniach"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nieużywana aplikacja"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Nieużywane aplikacje (<xliff:g id="NUMBER_OF_APPS">%s</xliff:g>)"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do zdjęć, filmów, muzyki, dźwięków i innych plików na tym urządzeniu?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do muzyki i innych plików audio na tym urządzeniu?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do zdjęć i filmów na tym urządzeniu?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Przyznać aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” dostęp do kolejnych zdjęć?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na nagrywanie dźwięku?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacja będzie mogła nagrywać dźwięk tylko wtedy, gdy będzie używana"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na nagrywanie dźwięku?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Pokazuj komunikat, gdy aplikacja uzyskuje dostęp do skopiowanego tekstu, obrazów lub innych treści"</string> <string name="show_password_title" msgid="2877269286984684659">"Pokazuj hasła"</string> <string name="show_password_summary" msgid="1110166488865981610">"Wpisywane znaki są przez chwilę wyświetlane"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ta aplikacja deklaruje, że może udostępniać dane typu <xliff:g id="PERMISSION_NAME">%s</xliff:g> osobom trzecim"</string> </resources> diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml index 70b6bb89b..a5d16dadb 100644 --- a/PermissionController/res/values-pt-rBR/strings.xml +++ b/PermissionController/res/values-pt-rBR/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto o app estiver em uso\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter \"Apenas esta vez\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dispensar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}one{# hora}many{# horas}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1min}one{#min}many{#min}other{#min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1s}one{#s}many{#s}other{#s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dia}one{# dia}many{# de dias}other{# dias}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}many{# de min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{#s}one{#s}many{# de segundos}other{#s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualquer permissão"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Qualquer horário"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 dias"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{No último dia}one{No último # dia}many{Nos últimos # de dias}other{Nos últimos # dias}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Na última # hora}one{Na última # hora}many{Nas últimas # de horas}other{Nas últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{No último # minuto}one{No último # minuto}many{Nos últimos # de minutos}other{Nos últimos # minutos}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nenhum uso de permissões"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acesso mais recente em qualquer momento"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acesso mais recente nos últimos sete dias"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso da permissão na última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso da permissão nos últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso da permissão no último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Permissão não usada nas últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Permissão não usada nos últimos sete dias"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Não usado no último # dia}one{Não usado no último # dia}many{Não usado nos últimos # de dias}other{Não usado nos últimos # dias}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Não usado na última # hora}one{Não usado na última # hora}many{Não usado nas últimas # de horas}other{Não usado nas últimas # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Permissões usadas por 1 app}one{Permissões usadas por # app}many{Used by # apps}other{Permissões usadas por # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver tudo no painel"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrado por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acesso apenas a arquivos de mídia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir o tempo todo"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir durante o uso do app"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"Não permitido"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que têm acesso a todos os arquivos"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}one{# hora}many{# horas}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}one{# minuto}many{# minutos}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}one{# segundo}many{# segundos}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}one{# minuto}many{# de minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}one{# segundo}many{# de segundos}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Lembretes de permissões"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app não usado"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps não usados"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse <b>fotos, vídeos, músicas, áudios e outros arquivos</b> neste dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse músicas e áudios neste dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse fotos e vídeos neste dispositivo?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar uma mensagem quando os apps acessarem textos, imagens ou outros conteúdos copiados"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar senhas"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostrar os caracteres rapidamente enquanto você digita"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml index a70346229..380264962 100644 --- a/PermissionController/res/values-pt-rPT/strings.xml +++ b/PermissionController/res/values-pt-rPT/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto a app está a ser utilizada”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter “Apenas desta vez”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais informação"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acesso a todas as fotos"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selecionar fotos"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selecionar mais fotos"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Não selecionar mais fotos"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignorar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dia}many{# dias}other{# dias}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dia}many{# dias}other{# dias}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualquer autorização"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Em qualquer altura"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 dias"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# dia anterior}many{# dias anteriores}other{# dias anteriores}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# hora anterior}many{# horas anteriores}other{# horas anteriores}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# minuto anterior}many{# minutos anteriores}other{# minutos anteriores}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Autorizações não utilizadas"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acesso mais recente em qualquer altura"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acesso mais recente nos últimos 7 dias"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Utilização das autorizações na última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Utilização das autorizações nos últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Utilização das autorizações no último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Sem utilização nas últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Não utilizada nos últimos 7 dias"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Não usada há # dia}many{Não usada há # dias}other{Não usada há # dias}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Não usada há # hora}many{Não usada há # horas}other{Não usada há # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilização: 1 app}many{Utilização: # apps}other{Utilização: # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver tudo no painel de controlo"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrado por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir apenas o acesso ao conteúdo multimédia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir sempre"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir apenas enquanto uso a app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permita todas as fotos"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permita fotos selecionadas"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Localização exata"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Não permitidas"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que podem aceder a todos os ficheiros"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}many{# dias}other{# dias}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}many{# horas}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}many{# minutos}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}many{# segundos}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# segundos}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Lembretes de autorização"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app não utilizada"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps não utilizadas"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que a app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda a <b>fotos, vídeos, música, áudio, etc.</b> neste dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que a app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda a música e áudio neste dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que a app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda a fotos e vídeos neste dispositivo?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Concede à app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesso a mais fotos?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que a app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"A app apenas poderá gravar áudio enquanto a estiver a utilizar."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que a app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Apresente uma mensagem quando as apps acedem a texto, imagens ou outro conteúdo que copiou"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar palavras-passe"</string> <string name="show_password_summary" msgid="1110166488865981610">"Apresente rapidamente os carateres ao escrever"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta app declarou que pode partilhar dados da autorização <xliff:g id="PERMISSION_NAME">%s</xliff:g> com terceiros"</string> </resources> diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml index 70b6bb89b..a5d16dadb 100644 --- a/PermissionController/res/values-pt/strings.xml +++ b/PermissionController/res/values-pt/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto o app estiver em uso\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter \"Apenas esta vez\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dispensar"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hora}one{# hora}many{# horas}other{# horas}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1min}one{#min}many{#min}other{#min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1s}one{#s}many{#s}other{#s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dia}one{# dia}many{# de dias}other{# dias}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}many{# de min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{#s}one{#s}many{# de segundos}other{#s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Qualquer permissão"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Qualquer horário"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Últimos 7 dias"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Últimas 24 horas"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Última hora"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Últimos 15 minutos"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Último minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{No último dia}one{No último # dia}many{Nos últimos # de dias}other{Nos últimos # dias}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Na última # hora}one{Na última # hora}many{Nas últimas # de horas}other{Nas últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{No último # minuto}one{No último # minuto}many{Nos últimos # de minutos}other{Nos últimos # minutos}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nenhum uso de permissões"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Acesso mais recente em qualquer momento"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Acesso mais recente nos últimos sete dias"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uso da permissão na última hora"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uso da permissão nos últimos 15 minutos"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uso da permissão no último minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Permissão não usada nas últimas 24 horas"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Permissão não usada nos últimos sete dias"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Não usado no último # dia}one{Não usado no último # dia}many{Não usado nos últimos # de dias}other{Não usado nos últimos # dias}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Não usado na última # hora}one{Não usado na última # hora}many{Não usado nas últimas # de horas}other{Não usado nas últimas # horas}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Permissões usadas por 1 app}one{Permissões usadas por # app}many{Used by # apps}other{Permissões usadas por # apps}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Ver tudo no painel"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrado por: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acesso apenas a arquivos de mídia"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir o tempo todo"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir durante o uso do app"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string> <string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"Não permitido"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que têm acesso a todos os arquivos"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hora}one{# hora}many{# horas}other{# horas}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}one{# minuto}many{# minutos}other{# minutos}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}one{# segundo}many{# segundos}other{# segundos}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}one{# minuto}many{# de minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}one{# segundo}many{# de segundos}other{# segundos}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Lembretes de permissões"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app não usado"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps não usados"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse <b>fotos, vídeos, músicas, áudios e outros arquivos</b> neste dispositivo?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse músicas e áudios neste dispositivo?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse fotos e vídeos neste dispositivo?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar uma mensagem quando os apps acessarem textos, imagens ou outros conteúdos copiados"</string> <string name="show_password_title" msgid="2877269286984684659">"Mostrar senhas"</string> <string name="show_password_summary" msgid="1110166488865981610">"Mostrar os caracteres rapidamente enquanto você digita"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml index 54d1b820e..ece525e84 100644 --- a/PermissionController/res/values-ro/strings.xml +++ b/PermissionController/res/values-ro/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Păstrează opțiunea „Când aplicația este folosită”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Păstrează „Doar de data aceasta”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mai multe info."</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permite accesul la toate fotografiile"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selectează fotografii"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selectează mai multe fotografii"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nu selecta mai multe fotografii"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Nu permite în nicio situație"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Închide"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> din <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{O zi}few{# zile}other{# de zile}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{O oră}few{# ore}other{# de ore}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min.}few{# min.}other{# min.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{O sec.}few{# sec.}other{# sec.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# zi}few{# zile}other{# de zile}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# oră}few{# ore}other{# de ore}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min.}few{# min.}other{# min.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}few{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Orice permisiune"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Oricând"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Ultimele 7 zile"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Ultimele 24 de ore"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Ultima oră"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Ultimele 15 minute"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Ultimul minut"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultima zi}few{Ultimele # zile}other{Ultimele # de zile}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ultima oră}few{Ultimele # ore}other{Ultimele # de ore}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ultimul minut}few{Ultimele # minute}other{Ultimele # de minute}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nicio permisiune folosită"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Cel mai recent acces oricând"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Cea mai recentă accesare din ultimele șapte zile"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Folosirea permisiunii în ultima oră"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Folosirea permisiunii în ultimele 15 minute"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Folosirea permisiunii în ultimul minut"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nu s-a(u) folosit în ultimele 24 de ore"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nu a fost folosită în ultimele șapte zile"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Permisiunea nu a fost folosită în ultima zi}few{Permisiunea nu a fost folosită în ultimele # zile}other{Permisiunea nu a fost folosită în ultimele # de zile}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Permisiunea nu a fost folosită în ultima oră}few{Permisiunea nu a fost folosită în ultimele # ore}other{Permisiunea nu a fost folosită în ultimele # de ore}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Folosită de o aplicație}few{Folosită de # aplicații}other{Folosită de # de aplicații}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Vezi totul în Tabloul de bord"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrat după: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permite accesul numai la fișierele media"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permite întotdeauna"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permite numai când folosești aplicația"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permite toate fotografiile"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permite fotografiile selectate"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Întreabă de fiecare dată"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nu permite"</string> <string name="precise_image_description" msgid="6349638632303619872">"Locația exactă"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nepermise"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Vezi mai multe aplicații care pot accesa toate fișierele"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{O zi}few{# zile}other{# de zile}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{O oră}few{# ore}other{# de ore}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{Un minut}few{# minute}other{# de minute}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{O secundă}few{# secunde}other{# de secunde}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# oră}few{# ore}other{# de ore}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}few{# minute}other{# de minute}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# secundă}few{# secunde}other{# de secunde}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Mementouri de permisiune"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"O aplicație nefolosită"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplicații nefolosite"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permiți accesul <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> la <b>fotografii, clipuri, conținut audio, muzică și alte fișiere</b> de pe dispozitiv?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permiți accesul <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> la muzică și fișiere audio de pe acest dispozitiv?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să acceseze fotografiile și videoclipurile de pe dispozitiv?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Permiți accesul <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> la mai multe fotografii?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să înregistreze audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplicația va putea să înregistreze conținut audio doar când o folosești"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să înregistreze audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afișează un mesaj când aplicațiile accesează text, imagini sau alte tipuri de conținut pe care le-ai copiat"</string> <string name="show_password_title" msgid="2877269286984684659">"Afișează parolele"</string> <string name="show_password_summary" msgid="1110166488865981610">"Caracterele se afișează pentru scurt timp, pe măsură ce tastezi"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplicația afirmă că poate trimite terților date despre <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string> </resources> diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml index cf107f854..e6dee7884 100644 --- a/PermissionController/res/values-ru/strings.xml +++ b/PermissionController/res/values-ru/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Оставить доступ только в активном режиме"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Оставить \"Только в этот раз\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Подробнее"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Разрешить доступ ко всем фото"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Выбрать фото"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Выбрать ещё фото"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Больше не выбирать фото"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Все равно запретить"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Закрыть"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> из <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 день}one{# день}few{# дня}many{# дней}other{# дня}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 час}one{# час}few{# часа}many{# часов}other{# часа}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 минута}one{# минута}few{# минуты}many{# минут}other{# минуты}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# день}one{# день}few{# дня}many{# дней}other{# дня}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# час}one{# час}few{# часа}many{# часов}other{# часа}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# минута}one{# минута}few{# минуты}many{# минут}other{# минуты}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Все разрешения"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Все время"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Последние 7 дней"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Последние 24 часа"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Последний час"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Последние 15 минут"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Последняя минута"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{За последний # день}one{За последний # день}few{За последние # дня}many{За последние # дней}other{За последние # дня}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{За последний # час}one{За последний # час}few{За последние # часа}many{За последние # часов}other{За последние # часа}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{За последнюю # минуту}one{За последнюю # минуту}few{За последние # минуты}many{За последние # минут}other{За последние # минуты}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Разрешения не использовались"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Доступ к разрешениям за все время"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Доступ за последние 7 дней"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Разрешения, использованные за последний час"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Разрешения, использованные за последние 15 минут"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Разрешения, использованные за последнюю минуту"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Не использовалось последние 24 часа"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Не использовалось последние 7 дней"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{С момента последнего использования прошел # день}one{С момента последнего использования прошел # день}few{С момента последнего использования прошло # дня}many{С момента последнего использования прошло # дней}other{С момента последнего использования прошло # дня}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{С момента последнего использования прошел # час}one{С момента последнего использования прошел # час}few{С момента последнего использования прошло # часа}many{С момента последнего использования прошло # часов}other{С момента последнего использования прошло # часа}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Использует 1 приложение}one{Использует # приложение}few{Используют # приложения}many{Используют # приложений}other{Используют # приложения}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Показать все в панели управления"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Фильтр: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Разрешить доступ только к медиафайлам"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Разрешить в любом режиме"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Разрешить только во время использования приложения"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Разрешить все фото"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Разрешить выбранные фото"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Спрашивать каждый раз"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Запретить"</string> <string name="precise_image_description" msgid="6349638632303619872">"Точное местоположение"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Доступ запрещен"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ещё приложения с доступом ко всем файлам"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 день}one{# день}few{# дня}many{# дней}other{# дня}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 час}one{# час}few{# часа}many{# часов}other{# часа}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минута}one{# минута}few{# минуты}many{# минут}other{# минуты}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}one{# час}few{# часа}many{# часов}other{# часа}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минута}one{# минута}few{# минуты}many{# минут}other{# минуты}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}one{# секунда}few{# секунды}many{# секунд}other{# секунды}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Напоминания о разрешениях"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 неиспользуемое приложение"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Неиспользуемые приложения (<xliff:g id="NUMBER_OF_APPS">%s</xliff:g>)"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к <b>фото, видео, аудио и другим файлам</b> на устройстве?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к музыке и аудио на устройстве?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к фото и видео на устройстве?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Предоставить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к другим фотографиям?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записывать аудио?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Приложение будет записывать аудио, только когда вы им пользуетесь."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записывать аудио?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Показывать уведомления, когда приложения обращаются к скопированному тексту, изображениям или другому контенту"</string> <string name="show_password_title" msgid="2877269286984684659">"Показывать пароли"</string> <string name="show_password_summary" msgid="1110166488865981610">"Ненадолго показывать символы при вводе"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Это приложение может передавать третьим лицам данные, относящиеся к категории \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\""</string> </resources> diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml index 5859b9652..8e4a7d397 100644 --- a/PermissionController/res/values-si/strings.xml +++ b/PermissionController/res/values-si/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“යෙදුම භාවිතයේ තිබෙන අතරතුර” තබා ගන්න"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“මේ වතාවේ පමණක්” තබා ගන්න"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"තවත් තතු"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"සියලු ඡායාරූප වෙත ප්රවේශ වීමට ඉඩ දෙන්න"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ඡායාරූප තෝරන්න"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"තවත් ඡායාරූප තෝරන්න"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"තවත් ඡායාරූප තෝරන්න එපා"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"කෙසේ වෙතත් ඉඩ නොදෙන්න"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ඉවත ලන්න"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>කින් <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{දින 1ක්}one{දින #ක්}other{දින #ක්}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{පැය 1ක්}one{පැය #ක්}other{පැය #ක්}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{මිනි 1ක්}one{මිනි #ක්}other{මිනි #ක්}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{තත් 1ක්}one{තත් #ක්}other{තත් #ක්}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# දිනක්}one{දින #ක්}other{දින #ක්}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{පැය #ක්}one{පැය #ක්}other{පැය #ක්}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{විනා #ක්}one{විනාඩි #ක්}other{විනාඩි #ක්}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{තත් #}one{තත් #ක්}other{තත් #ක්}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ඕනෑම අවසරයක්"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ඕනෑම වේලාවක"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"පසුගිය දින 7"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"පසුගිය පැය 24"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"පසුගිය පැය 1"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"පසුගිය මිනිත්තු 15"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"පසුගිය විනාඩි 1"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{අවසාන දින #}one{අවසාන දින #}other{අවසාන දින #}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{පසුගිය පැය #}one{පසුගිය පැය #}other{පසුගිය පැය #}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{පසුගිය මිනිත්තු #}one{පසුගිය මිනිත්තු #}other{පසුගිය මිනිත්තු #}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"අවසර භාවිත නැත"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ඕනෑම වේලාවක ඉතාම මෑත ප්රවේශය"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"අවසාන දින 7 ක් තුළ ඉතා මෑත ප්රවේශය"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"අවසාන 1 පැයක් තුළ අවසර භාවිතය"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"අවසාන විනාඩි 15 තුළ අවසර භාවිතය"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"අවසාන 1 විනාඩිය තුළ අවසර භාවිතය"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"පසුගිය පැය 24 තුළ භාවිත කර නැත"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"පසුගිය දින 7 තුළ භාවිත කර නැත"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{පසුගිය දින # තුළ භාවිතා කර නැත}one{පසුගිය දින # තුළ භාවිතා කර නැත}other{පසුගිය දින # තුළ භාවිතා කර නැත}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{පසුගිය පැය # තුළ භාවිතා කර නැත}one{පසුගිය පැය # තුළ භාවිතා කර නැත}other{පසුගිය පැය # තුළ භාවිතා කර නැත}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{යෙදුම් 1කින් භාවිත කෙරේ}one{යෙදුම් #කින් භාවිත කෙරේ}other{යෙදුම් #කින් භාවිත කෙරේ}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"උපකරණ පුවරුවේ ඇති සියල්ල බලන්න"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"මේ අනුව පෙරහන්න: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"මාධ්ය වෙත ප්රවේශය පමණක් ඉඩ දෙන්න"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"සැම විටම ඉඩ දෙන්න"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"යෙදුම භාවිතයේදී පමණක් ඉඩ දෙන්න"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ඡායාරූප සියල්ලට අවසර දෙන්න"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"තෝරන ලද ඡායාරූපවලට අවසර දෙන්න"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"සෑම විටම ඉල්ලන්න"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ඉඩ නොදෙන්න"</string> <string name="precise_image_description" msgid="6349638632303619872">"ඉතා නිවැරදි ස්ථානය"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ඉඩ නොදේ"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"සියලු ගොනු වෙත ප්රවේශ විය හැකි තව යෙදුම් බලන්න"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{දින 1ක්}one{දින #ක්}other{දින #ක්}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{පැය 1ක්}one{පැය #ක්}other{පැය #ක්}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{මිනිත්තු 1ක්}one{මිනිත්තු #ක්}other{මිනිත්තු #ක්}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{තත්පර 1ක්}one{තත්පර #ක්}other{තත්පර #ක්}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{පැය #ක්}one{පැය #ක්}other{පැය #ක්}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{මිනිත්තු #ක්}one{මිනිත්තු #ක්}other{මිනිත්තු #ක්}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{තත්පර #}one{තත්පර #}other{තත්පර #}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"අවසර සිහි කැඳවීම්"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"භාවිත නොකළ යෙදුම් 1"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"භාවිත නොකළ යෙදුම් <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> හට මෙම උපාංගයේ <b>ඡායාරූප, වීඩියෝ, සංගීතය, ශ්රව්ය සහ වෙනත් ගොනු<b> වෙත ප්රවේශ වීමට ඉඩ දෙන්නද?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> හට මෙම උපාංගයේ සංගීතය සහ ශ්රව්ය වෙත ප්රවේශ වීමට ඉඩ දෙන්නද?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> හට මෙම උපාංගයේ ඇති ඡායාරූප සහ වීඩියෝ වෙත ප්රවේශ වීමට ඉඩ දෙන්නද?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> හට තවත් ඡායාරූප වෙත ප්රවේශය දෙන්නද?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> වෙත ශබ්දය පටි ගත කිරීමට ඉඩ දෙන්නද?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"යෙදුමට ඔබ යෙදුම භාවිත කරන අතරතුර ඕඩියෝ පටිගත කිරීමට පමණක් හැකි වනු ඇත"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> හට ඕඩියෝ පටිගත කිරීමට ඉඩ දෙන්නද?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"යෙදුම් ඔබ පිටපත් කර ඇති පාඨ, රූප හෝ වෙනත් අන්තර්ගතය වෙත ප්රවේශ වන විට පණිවුඩයක් පෙන්වන්න"</string> <string name="show_password_title" msgid="2877269286984684659">"මුරපද පෙන්වන්න"</string> <string name="show_password_summary" msgid="1110166488865981610">"ඔබ ටයිප් කරන විට අනුලකුණු කෙටියෙන් පෙන්වන්න"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"මෙම යෙදුම තෙවන පාර්ශව සමග <xliff:g id="PERMISSION_NAME">%s</xliff:g> දත්ත බෙදා ගත හැකි යැයි මෙය සඳහන් කළා"</string> </resources> diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml index bf5caf0a6..6ecde85cf 100644 --- a/PermissionController/res/values-sk/strings.xml +++ b/PermissionController/res/values-sk/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Ponechať Počas používania aplikácie"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Ponechať možnosť Iba tentokrát"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ďalšie info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Povoliť prístup k všetkým fotkám"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vybrať fotky"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vybrať ďalšie fotky"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nevyberať ďalšie fotky"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Aj tak nepovoliť"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Zavrieť"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 deň}few{# dni}many{# dňa}other{# dní}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodín}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}few{# min}many{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}few{# s}many{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# deň}few{# dni}many{# dňa}other{# dní}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hodina}few{# hodiny}many{# hodiny}other{# hodín}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}few{# min}many{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}few{# s}many{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Všetky povolenia"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Kedykoľvek"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Posledných 7 dní"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Posledných 24 hodín"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Posledná 1 hodina"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Posledných 15 minút"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Posledná 1 minúta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Posledný # deň}few{Posledné # dni}many{Posledných # dňa}other{Posledných # dní}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Posledná # hodina}few{Posledné # hodiny}many{Posledných # hodiny}other{Posledných # hodín}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Posledná # minúta}few{Posledné # minúty}many{Posledných # minúty}other{Posledných # minút}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Žiadne využitie povolení"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Posledný prístup kedykoľvek"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Posledný prístup za posledných sedem dní"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Používanie povolení za poslednú hodinu"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Používanie povolení za posledných 15 minút"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Používanie povolení za poslednú minútu"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Nepoužité za posledných 24 hodín"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nepoužité za posledných 7 dní"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nepoužité za posledný # deň}few{Nepoužité za posledné # dni}many{Nepoužité za posledných # dňa}other{Nepoužité za posledných # dní}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nepoužité za poslednú # hodinu}few{Nepoužité za posledné # hodiny}many{Nepoužité za posledných # hodiny}other{Nepoužité za posledných # hodín}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Používa 1 aplikácia}few{Používajú # aplikácie}many{Used by # apps}other{Používa # aplikácií}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Zobraziť všetko v hlavnom paneli"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrované podľa: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Povoliť prístup iba k médiám"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Povoliť vždy"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Povoliť iba pri používaní aplikácie"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Povoliť všetky fotky"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Povoliť vybrané fotky"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Vždy sa opýtať"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovoliť"</string> <string name="precise_image_description" msgid="6349638632303619872">"Presná poloha"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nepovolené"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobraziť ďalšie aplikácie s prístupom k všetkým súborom"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 deň}few{# dni}many{# dňa}other{# dní}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodín}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minúta}few{# minúty}many{# minúty}other{# minút}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunda}few{# sekundy}many{# sekundy}other{# sekúnd}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hodina}few{# hodiny}many{# hodiny}other{# hodín}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minúta}few{# minúty}many{# minúty}other{# minút}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunda}few{# sekundy}many{# sekundy}other{# sekúnd}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Pripomenutia povolení"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 nepoužívaná aplikácia"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Nepoužívané aplikácie: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup k <b>fotkám, videám, hudbe, zvuku a ďalším súborom</b> v tomto zariadení?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Chcete povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup k hudbe a zvuku v tomto zariadení?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Chcete povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup k fotkám a videám v tomto zariadení?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Chcete udeliť aplikácii <xliff:g id="APP_NAME">%1$s</xliff:g> prístup k ďalším fotkám?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Chcete povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> nahrávať zvuk?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Táto aplikácia bude môcť nahrávať zvuk iba vtedy, keď ju budete používať"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Chcete povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> nahrávať zvuk?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovať správu, keď sa aplikácie získajú pristup k textu, obrázkom alebo inému obsahu, ktorý ste skopírovali"</string> <string name="show_password_title" msgid="2877269286984684659">"Zobrazovať heslá"</string> <string name="show_password_summary" msgid="1110166488865981610">"Pri písaní nakrátko zobrazovať zadávané znaky"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Táto aplikácia uvádzala, že môže zdieľať údaje povolenia <xliff:g id="PERMISSION_NAME">%s</xliff:g> s tretími stranami"</string> </resources> diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml index fb8fc505d..ef2e75e3e 100644 --- a/PermissionController/res/values-sl/strings.xml +++ b/PermissionController/res/values-sl/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Obdrži »Ko je aplikacija v uporabi«"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Obdrži »Samo tokrat«"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Več informacij"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dovoli dostop do vseh fotografij"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Izbira fotografij"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Izbira več fotografij"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Brez izbire več fotografij"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne dovoli kljub temu"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Opusti"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dan}one{# dan}two{# dneva}few{# dni}other{# dni}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ura}one{# ura}two{# uri}few{# ure}other{# ur}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}two{# min}few{# min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 s}one{# s}two{# s}few{# s}other{# s}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dan}one{# dan}two{# dneva}few{# dni}other{# dni}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ura}one{# ura}two{# uri}few{# ure}other{# ur}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}one{# min}two{# min}few{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# s}one{# s}two{# s}few{# s}other{# s}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Katero koli dovoljenje"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Kadar koli"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Zadnjih 7 dni"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Zadnjih 24 ur"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Zadnja ura"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Zadnjih 15 minut"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Zadnja minuta"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Zadnji # dan}one{Zadnji # dan}two{Zadnja # dneva}few{Zadnje # dni}other{Zadnjih # dni}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Zadnja # ura}one{Zadnja # ura}two{Zadnji # uri}few{Zadnje # ure}other{Zadnjih # ur}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Zadnja # minuta}one{Zadnja # minuta}two{Zadnji # minuti}few{Zadnje # minute}other{Zadnjih # minut}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ni uporabe dovoljenj"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Zadnja uporaba kadar koli"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Zadnja uporaba v zadnjih 7 dneh"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Uporaba dovoljenj v zadnji uri"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Uporaba dovoljenj v zadnjih 15 minutah"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Uporaba dovoljenj v zadnji minuti"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Brez uporabe v zadnjih 24 urah"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Brez uporabe v zadnjih 7 dneh"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Brez uporabe v zadnjem # dnevu}one{Brez uporabe v zadnjem # dnevu}two{Brez uporabe v zadnjih # dneh}few{Brez uporabe v zadnjih # dneh}other{Brez uporabe v zadnjih # dneh}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Brez uporabe v zadnji # uri}one{Brez uporabe v zadnji # uri}two{Brez uporabe v zadnjih # urah}few{Brez uporabe v zadnjih # urah}other{Brez uporabe v zadnjih # urah}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Uporablja 1 aplikacija}one{Uporablja # aplikacija}two{Uporabljata # aplikaciji}few{Uporabljajo # aplikacije}other{Uporablja # aplikacij}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Prikaži vse na nadzorni plošči"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrirano po: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dovoli samo dostop do predstavnosti"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vedno dovoli"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dovoli samo med uporabo aplikacije"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dovoli vse fotografije"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dovoli izbrane fotografije"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Vedno vprašaj"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dovoli"</string> <string name="precise_image_description" msgid="6349638632303619872">"Natančna lokacija"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ni dovoljeno"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Prikaži več aplikacij z dostopom do vseh datotek"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}two{# dneva}few{# dni}other{# dni}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ura}one{# ura}two{# uri}few{# ure}other{# ur}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuta}one{# minuta}two{# minuti}few{# minute}other{# minut}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekunda}one{# sekunda}two{# sekundi}few{# sekunde}other{# sekund}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ura}one{# ura}two{# uri}few{# ure}other{# ur}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuta}one{# minuta}two{# minuti}few{# minute}other{# minut}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekunda}one{# sekunda}two{# sekundi}few{# sekunde}other{# sekund}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Opomniki za dovoljenja"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 neuporabljena aplikacija"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Št. neuporabljenih aplikacij: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dovolite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> dostop do <b>fotografij, videoposnetkov, glasbe, zvočnih datotek in drugih datotek</b> v tej napravi?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dovolite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> dostop do glasbe in zvočnih datotek v tej napravi?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dovolite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> dostop do fotografij in videoposnetkov v tej napravi?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Ali aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> dovolite dostop do več fotografij?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dovolite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snemanje zvoka?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija bo lahko snemala zvok le med vašo uporabo aplikacije."</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> omogočiti snemanje zvoka?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Pokaži sporočilo, ko aplikacije dostopijo do besedila, slik ali drugih vsebin, ki ste jih kopirali."</string> <string name="show_password_title" msgid="2877269286984684659">"Pokaži gesla"</string> <string name="show_password_summary" msgid="1110166488865981610">"Za trenutek prikaži znake med vnašanjem."</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ta aplikacija navaja, da bo podatke o dovoljenju »<xliff:g id="PERMISSION_NAME">%s</xliff:g>« morda delila s tretjimi osebami."</string> </resources> diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml index 3b6e72247..33f575884 100644 --- a/PermissionController/res/values-sq/strings.xml +++ b/PermissionController/res/values-sq/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mbaje \"Kur aplikacioni është në përdorim\""</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mbaje “Vetëm këtë herë”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Më shumë info."</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Lejo qasjen te të gjitha fotografitë"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Zgjidh fotografitë"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Zgjidh fotografi të tjera"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Mos zgjidh fotografi të tjera"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Mos lejo gjithsesi"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Hiqe"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> nga <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ditë}other{# ditë}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 orë}other{# orë}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min.}other{# min.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek.}other{# sek.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ditë}other{# ditë}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# orë}other{# orë}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min.}other{# min.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek.}other{# sek.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Çdo autorizim"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Në çdo kohë"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 ditët e fundit"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 orët e fundit"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 orën e fundit"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 minutat e fundit"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Minuta e fundit"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Dita e fundit}other{# ditët e fundit}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ora e fundit}other{# orët e fundit}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Minuta e fundit}other{# minutat e fundit}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Nuk ka përdorime të lejeve"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Qasja më e fundit në çdo kohë"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Qasja më e fundit në 7 ditët e fundit"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Përdorimi i autorizimeve në 1 orën e fundit"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Përdorimi i autorizimeve në 15 minutat e fundit"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Përdorimi i autorizimeve në 1 minutën e fundit"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Asnjë përdorim gjatë 24 orëve të fundit"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Nuk është përdorur gjatë 7 ditëve të fundit"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Nuk është përdorur gjatë ditës së fundit}other{Nuk është përdorur gjatë # ditëve të fundit}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Nuk është përdorur gjatë orës së fundit}other{Nuk është përdorur gjatë # orëve të fundit}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Përdorur nga 1 aplikacion}other{Përdorur nga # aplikacione}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Shikoji të gjitha te \"Paneli\""</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtruar sipas: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Lejo qasjen vetëm në media"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Lejo gjithmonë"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Lejo vetëm kur përdor aplikacionin"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Lejo të gjitha fotografitë"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Lejo fotografitë e zgjedhura"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Pyet çdo herë"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Mos lejo"</string> <string name="precise_image_description" msgid="6349638632303619872">"Vendndodhja e saktë"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Nuk lejohet"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Shiko më shumë aplikacione me qasje tek të gjithë skedarët"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ditë}other{# ditë}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 orë}other{# orë}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minutë}other{# minuta}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekondë}other{# sekonda}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# orë}other{# orë}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minutë}other{# minuta}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekondë}other{# sekonda}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Alarmet rikujtuese për lejet"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 aplikacion i papërdorur"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> aplikacione të papërdorura"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Të lejohet që <b&gt<xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te <b>fotografitë, videot, muzika, audioja e të tjera</b> në pajisje?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te muzika dhe te audioja në këtë pajisje?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te fotografitë dhe videot në këtë pajisje?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"T\'i jepet <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> qasje në fotografi të tjera?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të regjistrojë audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacioni do të mund të regjistrojë audion vetëm kur ti po e përdor aplikacionin"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të regjistrojë audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Shfaq një mesazh kur aplikacionet qasen te tekstet, imazhet ose përmbajtje të tjera që ke kopjuar"</string> <string name="show_password_title" msgid="2877269286984684659">"Shfaq fjalëkalimet"</string> <string name="show_password_summary" msgid="1110166488865981610">"Shfaq karakteret shkurtimisht kur shkruan"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ky aplikacion deklaroi se mund të ndajë të dhënat e \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\" me palë të treta"</string> </resources> diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml index 21859c0e6..fd4e1a452 100644 --- a/PermissionController/res/values-sr/strings.xml +++ b/PermissionController/res/values-sr/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Задржи „Док се апликација користи“"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Задржи Само овај пут"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Више информација"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дозволи приступ свим сликама"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Изаберите слике"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Изаберите још слика"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Немојте да бирате више слика"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ионако не дозволи"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Одбаци"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 дан}one{# дан}few{# дана}other{# дана}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 сат}one{# сат}few{# сата}other{# сати}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 мин}one{# мин}few{# мин}other{# мин}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 сек}one{# сек}few{# сек}other{# сек}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# дан}one{# дан}few{# дана}other{# дана}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# сат}one{# сат}few{# сата}other{# сати}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# мин}one{# мин}few{# мин}other{# мин}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# сек}one{# сек}few{# сек}other{# сек}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Било која дозвола"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Било када"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Последњих 7 дана"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Последња 24 сата"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Последњи сат"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Последњих 15 минута"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Последњи минут"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Последњи # дан}one{Последњи # дан}few{Последња # дана}other{Последњих # дана}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Последњи # сат}one{Последњи # сат}few{Последња # сата}other{Последњих # сати}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Последњи # минут}one{Последњи # минут}few{Последња # минута}other{Последњих # минута}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Дозволе нису коришћене"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Најскорији приступ у било ком тренутку"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Најскорији приступ у последњих 7 дана"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Коришћење дозвола у последњих сат времена"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Коришћење дозволе у последњих 15 минута"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Коришћење дозвола у последњем минуту"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Није коришћено у последња 24 сата"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Није коришћено у последњих 7 дана"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Није коришћено током последњег # дана}one{Није коришћено током последњег # дана}few{Није коришћено током последња # дана}other{Није коришћено током последњих # дана}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Није коришћено током последњег # сата}one{Није коришћено током последњег # сата}few{Није коришћено током последња # сата}other{Није коришћено током последњих # сати}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Користи 1 апликација}one{Користи # апликација}few{Користе # апликације}other{Користи # апликација}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Прикажи све на контролној табли"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Филтрирано према: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дозволи само приступ медијима"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволи увек"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозв. само док се апл. користи"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дозволи све слике"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дозволи изабране слике"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Питај сваки пут"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволи"</string> <string name="precise_image_description" msgid="6349638632303619872">"Прецизна локација"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Није дозвољено"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Прикажи још апликација са приступом свим фајловима"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 дан}one{# дан}few{# дана}other{# дана}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 сат}one{# сат}few{# сата}other{# сати}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 минут}one{# минут}few{# минута}other{# минута}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}one{# секунда}few{# секунде}other{# секунди}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# сат}one{# сат}few{# сата}other{# сати}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# минут}one{# минут}few{# минута}other{# минута}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}one{# секунда}few{# секунде}other{# секунди}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Подсетници за дозволе"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 апликација која се не користи"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Апликација које се не користе: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Приступ сликама, видеу, музици, звуку и другом на уређају за <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дозвољавате ли приступ музици и звуку на овом уређају за <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дозвољавате ли приступ сликама и видеу на овом уређају за <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Желите ли да апликацији <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> дозволите приступ већем броју слика?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима звук?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Апликација ће моћи да снима звук само док користите апликацију"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима звук?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Приказује поруку када апликације приступају тексту, сликама или другом садржају који сте копирали"</string> <string name="show_password_title" msgid="2877269286984684659">"Приказуј лозинке"</string> <string name="show_password_summary" msgid="1110166488865981610">"Приказује знакове накратко док куцате"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ова апликација наводи да може да дели податке (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) са трећим странама"</string> </resources> diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml index e5827cccb..d1e2ad1fe 100644 --- a/PermissionController/res/values-sv/strings.xml +++ b/PermissionController/res/values-sv/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behåll När appen används"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behåll Bara den här gången"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Ge åtkomst till alla foton"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Välj foton"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Välj fler foton"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Välj inte fler foton"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tillåt inte ändå"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Stäng"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 dag}other{# dagar}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 timme}other{# timmar}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}other{# min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sek}other{# sek}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# dag}other{# dagar}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# timme}other{# timmar}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# min}other{# min}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sek}other{# sek}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Alla behörigheter"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"När som helst"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Senaste 7 dagarna"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"De senaste 24 timmarna"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Senaste timmen"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"De senaste 15 minuterna"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Senaste minuten"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Senaste # dagen}other{Senaste # dagarna}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Senaste # timmen}other{Senaste # timmarna}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Senaste # minuten}other{Senaste # minuterna}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ingen behörighetsanvändning"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Den senaste åtkomsten när som helst"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Den senaste åtkomsten under de senaste sju dagarna"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Behörighetsanvändning under den senaste timmen"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Behörighetsanvändning under den senaste kvarten"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Behörighetsanvändning under den senaste minuten"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Har inte använts under de senaste 24 timmarna"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Har inte använts under de senaste sju dagarna"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Har inte använts under den senaste # dagen}other{Har inte använts under de senaste # dagarna}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Har inte använts under den senaste # timmen}other{Har inte använts under de senaste # timmarna}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Används av 1 app}other{Används av # appar}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Visa alla i översikten"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtreras efter: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Tillåt endast åtkomst till media"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillåt alltid"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Tillåt bara när appen används"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillåt alla foton"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillåt utvalda foton"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Fråga varje gång"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Tillåt inte"</string> <string name="precise_image_description" msgid="6349638632303619872">"Exakt plats"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Tillåts inte"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se fler appar som kan komma åt alla filer"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dagar}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 timme}other{# timmar}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minut}other{# minuter}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 sekund}other{# sekunder}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# timme}other{# timmar}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minut}other{# minuter}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# sekund}other{# sekunder}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Behörighetspåminnelser"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 app som inte används"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> appar som inte används"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till <b>foton, videor, musik, ljud och andra filer</b> på enheten?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till musik och ljud på enheten?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till foton och videor på enheten?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till fler foton?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> behörighet att spela in ljud?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bara spela in ljud medan du använder den"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> behörighet att spela in ljud?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Visa ett meddelande när appar får åtkomst till text, bilder eller annat som du har kopierat"</string> <string name="show_password_title" msgid="2877269286984684659">"Visa lösenord"</string> <string name="show_password_summary" msgid="1110166488865981610">"Visa tecknen en kort stund medan du skriver"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Appen har angett att den kan dela <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredje part"</string> </resources> diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml index 13080affe..bc7be147f 100644 --- a/PermissionController/res/values-sw/strings.xml +++ b/PermissionController/res/values-sw/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Usibadilishe “Wakati programu inatumika”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Usibadilishe “Wakati huu pekee”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Maelezo zaidi"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Usiruhusu hata hivyo"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ondoa"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kati ya <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{Siku moja}other{Siku #}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{Saa moja}other{Saa #}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{Dak 1}other{Dak #}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{Sek 1}other{Sek #}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{Siku #}other{Siku #}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{Saa #}other{Saa #}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{Dak #}other{Dak #}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{Sek #}other{Sek #}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Ruhusa yoyote"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Wakati wowote"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Siku 7 zilizopita"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Saa 24 zilizopita"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Saa 1 iliyopita"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Dakika 15 zilizopita"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Dakika 1 iliyopita"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Siku # iliyopita}other{Siku # zilizopita}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Saa # iliyopita}other{Saa # zilizopita}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Dakika # iliyopita}other{Dakika # zilizopita}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Hakuna matumizi ya ruhusa"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Zinazotumia zaidi wakati wowote"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Zilizotumiwa zaidi katika siku 7 zilizopita"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Ruhusa zilizotumiwa zaidi katika saa 1 iliyopita"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Matumizi ya ruhusa katika dakika 15 zilizopita."</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Matumizi ya ruhusa katika dakika 1 iliyopita"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Haijatumiwa katika saa 24 zilizopita"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Haijatumiwa katika siku saba zilizopita"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Haijatumiwa katika siku # iliyopita}other{Haijatumiwa katika siku # zilizopita}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Haijatumiwa katika saa # iliyopita}other{Haijatumiwa katika saa # zilizopita}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Imetumiwa na programu moja}other{Imetumiwa na programu #}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Yaone yote kwenye Dashibodi"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Zilizochujwa kulingana na: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Ruhusu ufikiaji wa maudhui pekee"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Ruhusu kila wakati"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Ruhusu tu unapotumia programu"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"Uliza kila wakati"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Usiruhusu"</string> <string name="precise_image_description" msgid="6349638632303619872">"Eneo mahususi"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"Zisizoruhusiwa"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Angalia programu zaidi zinazoweza kufikia faili zote"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{Siku moja}other{Siku #}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{Saa moja}other{Saa #}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{Dakika moja}other{Dakika #}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{Sekunde 1}other{Sekunde #}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{Saa #}other{Saa #}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{Dakika #}other{Dakika #}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{Sekunde #}other{Sekunde #}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Vikumbusho vya ruhusa"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Programu moja isiyotumika"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Programu <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> zisizotumika"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie <b>picha, video, muziki, sauti na faili zingine</b> kwenye kifaa hiki?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie muziki na sauti kwenye kifaa hiki?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie picha na video kwenye kifaa hiki?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> kurekodi sauti?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programu itaweza kurekodi sauti unapoitumia tu"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> irekodi sauti?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Onyesha ujumbe programu zinapofikia maandishi, picha au maudhui mengine uliyonakili"</string> <string name="show_password_title" msgid="2877269286984684659">"Onyesha manenosiri"</string> <string name="show_password_summary" msgid="1110166488865981610">"Onyesha herufi kwa muda mfupi unapoandika"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml index d5c87a0c3..0ff082c43 100644 --- a/PermissionController/res/values-ta/strings.xml +++ b/PermissionController/res/values-ta/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ஆப்ஸ் உபயோகத்தில் இருக்கும்போது” என்று வைக்கவும்"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"”இப்போது மட்டும்” வைத்திரு"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"மேலும் தகவல்"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"பரவாயில்லை, அனுமதிக்க வேண்டாம்"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"நிராகரி"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 நாள்}other{# நாட்கள்}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 மணிநேரம்}other{# மணிநேரம்}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 நிமிடம்}other{# நிமிடங்கள்}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 வினாடி}other{# வினாடிகள்}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# நாள்}other{# நாட்கள்}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# மணிநேரம்}other{# மணிநேரம்}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# நிமிடம்}other{# நிமிடங்கள்}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# வினாடி}other{# வினாடிகள்}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"அனுமதி எதுவாயினும்"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"எந்த நேரமும்"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"கடந்த 7 நாட்கள்"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"கடந்த 24 மணிநேரம்"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"கடந்த ஒரு மணிநேரம்"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"கடந்த 15 நிமிடங்கள்"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"கடந்த 1 நிமிடத்தில்"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{கடந்த # நாள்}other{கடந்த # நாட்கள்}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{கடந்த # மணிநேரம்}other{கடந்த # மணிநேரம்}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{கடந்த # நிமிடம்}other{கடந்த # நிமிடங்கள்}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"உபயோகிக்கப்படாத அனுமதிகள்"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"மிகச் சமீபத்திய அணுகல்"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"கடந்த 7 நாட்களில் மிகச் சமீபத்திய அணுகல்"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"கடந்த 1 மணிநேரத்தில் அணுகல் உபயோகம்"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"கடந்த 15 நிமிடங்களில் அணுகல் உபயோகம்"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"கடந்த 1 நிமிடத்தில் அணுகல் உபயோகம்"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"கடந்த 24 மணிநேரத்தில் பயன்படுத்தப்படவில்லை"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"கடந்த 7 நாட்களில் பயன்படுத்தப்படவில்லை"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{கடந்த # நாளில் பயன்படுத்தப்படவில்லை}other{கடந்த # நாட்களில் பயன்படுத்தப்படவில்லை}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{கடந்த # மணிநேரத்தில் பயன்படுத்தப்படவில்லை}other{கடந்த # மணிநேரத்தில் பயன்படுத்தப்படவில்லை}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ஆப்ஸால் பயன்படுத்தப்பட்டது}other{# ஆப்ஸால் பயன்படுத்தப்பட்டது}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"டாஷ்போர்டில் அனைத்தையும் காட்டு"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"இதன்படி வடிகட்டப்பட்டது: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"மீடியா ஃபைல்களை மட்டும் அணுக அனுமதி"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"எப்போதும் அனுமதி"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ஆப்ஸை உபயோகிக்கும்போது மட்டும் அனுமதி"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"ஒவ்வொரு முறையும் கேள்"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"அனுமதிக்க வேண்டாம்"</string> <string name="precise_image_description" msgid="6349638632303619872">"துல்லியமான இருப்பிடம்"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"அனுமதிக்கப்படாதவை"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"அனைத்து ஃபைல்களையும் அணுகக்கூடிய கூடுதல் ஆப்ஸைப் பாருங்கள்"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 நாள்}other{# நாட்கள்}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 மணிநேரம்}other{# மணிநேரம்}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 நிமிடம்}other{# நிமிடங்கள்}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 வினாடி}other{# வினாடிகள்}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# மணிநேரம்}other{# மணிநேரம்}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# நிமிடம்}other{# நிமிடங்கள்}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# வினாடி}other{# வினாடிகள்}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"அனுமதிக்கான நினைவூட்டல்கள்"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 பயன்படுத்தாத ஆப்ஸ்"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> பயன்படுத்தாத ஆப்ஸ்"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"சாதனத்திலுள்ள <b>படம், வீடியோ, இசை, ஆடியோ & பிற ஃபைல்களின்</b> அணுகலை <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸுக்கு வழங்கவா?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"இந்தச் சாதனத்திலுள்ள இசை மற்றும் ஆடியோவுக்கான அணுகலை <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸுக்கு வழங்கவா?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"இந்தச் சாதனத்திலுள்ள படங்கள் மற்றும் வீடியோக்களுக்கான அணுகலை <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸுக்கு வழங்கவா?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ஆடியோ ரெக்கார்டு செய்ய <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"இந்த ஆப்ஸை நீங்கள் உபயோகிக்கும்போது மட்டுமே ஆடியோ ரெக்கார்டு செய்யும்"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ஆடியோ ரெக்கார்டு செய்ய <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"நீங்கள் நகலெடுத்த வார்த்தைகளையோ படங்களையோ பிறவற்றையோ ஆப்ஸ் அணுகும்போது ஓர் அறிவிப்பைக் காட்டும்"</string> <string name="show_password_title" msgid="2877269286984684659">"கடவுச்சொற்களைக் காட்டுதல்"</string> <string name="show_password_summary" msgid="1110166488865981610">"டைப் செய்யும்போதே எழுத்துகளைச் சற்று நேரம் காட்டும்"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml index f4ed699ee..0c0f957c8 100644 --- a/PermissionController/res/values-te/strings.xml +++ b/PermissionController/res/values-te/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“యాప్ వినియోగంలో ఉన్నప్పుడు” నిలిపి ఉంచు"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“కేవలం ఈసారి మాత్రమే” ఇలాగే ఉంచు"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"మరింత సమాచారం"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ఫోటోలన్నింటికీ యాక్సెస్ను అనుమతించండి"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ఫోటోలను ఎంచుకోండి"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"మరిన్ని ఫోటోలను ఎంచుకోండి"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"మరిన్ని ఫోటోలను ఎంచుకోవద్దు"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ఏదేమైనా అనుమతించవద్దు"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"విస్మరించు"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> యొక్క <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 రోజు}other{# రోజులు}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 గంట}other{# గంటలు}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 నిమి}other{# నిమిషాలు}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 సెకను}other{# సెకన్లు}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# రోజు}other{# రోజులు}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# గంట}other{# గంటలు}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# నిమి}other{# నిమిషాలు}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# సెక}other{# సెకన్లు}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ఏ అనుమతి అయినా"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ఎప్పుడైనా"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"గత 7 రోజులు"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"గత 24 గంటలు"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"గత 1 గంట"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"గత 15 నిమిషాలు"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"చివరి 1 నిమిషం"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{గడిచిన # రోజులో}other{గడిచిన # రోజులలో}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{గడిచిన # గంటలో}other{గడిచిన # గంటలలో}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{గడిచిన # నిమిషంలో}other{గడిచిన # నిమిషాలలో}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"అనుమతి వినియోగాలేవీ లేవు"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"ఏ సమయంలోనైనా అత్యంత ఇటీవలి యాక్సెస్"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"గత 7 రోజులలో అత్యంత ఇటీవలి యాక్సెస్లు"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"గడిచిన ఒక గంటలో అనుమతి వినియోగించబడింది"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"గత 15 నిమిషాలలో అనుమతి వినియోగించబడింది"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"గత నిమిషంలో అనుమతి వినియోగించబడింది"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"గత 24 గంటలలో ఉపయోగించలేదు"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"గత 7 రోజుల్లో ఉపయోగించలేదు"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{గత # రోజులో ఉపయోగించలేదు}other{గత # రోజుల్లో ఉపయోగించలేదు}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{గత # గంటలో ఉపయోగించలేదు}other{గత # గంటలలో ఉపయోగించలేదు}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 యాప్ ద్వారా ఉపయోగించబడింది}other{# యాప్ల ద్వారా ఉపయోగించబడింది}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"అన్నింటినీ డాష్బోర్డ్లో చూడండి"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"దీని ద్వారా ఫిల్టర్ చేయబడింది: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"మీడియాకు మాత్రమే యాక్సెస్ను అనుమతించండి"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ఎల్లప్పుడూ అనుమతించండి"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే అనుమతించండి"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ఫోటోలన్నింటినీ అనుమతించండి"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ఎంచుకోబడిన ఫోటోలను అనుమతించండి"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ప్రతిసారి అడగాలి"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"అనుమతించవద్దు"</string> <string name="precise_image_description" msgid="6349638632303619872">"ఖచ్చితమైన లొకేషన్"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"అనుమతించబడలేదు"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"అన్ని ఫైళ్లను యాక్సెస్ చేయగల మరిన్ని యాప్లను చూడండి"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 రోజు}other{# రోజులు}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 గంట}other{# గంటలు}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 నిమిషం}other{# నిమిషాలు}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 సెకను}other{# సెకన్లు}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# గంట}other{# గంటలు}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# నిమిషం}other{# నిమిషాలు}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# సెకను}other{# సెకన్లు}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"అనుమతి రిమైండర్లు"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ఉపయోగించని యాప్"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ఉపయోగించని యాప్లు"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ఈపరికరంలో <b>ఫోటోలు, వీడియోలు, మ్యూజిక్, ఆడియో, ఇతర ఫైళ్ల</b> యాక్సెస్కు <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ఈ పరికరంలో మ్యూజిక్ను, ఆడియోను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ఈ పరికరంలో ఫోటోలను, వీడియోలను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>కు మరిన్ని ఫోటోలను యాక్సెస్ చేయడానికి అనుమతి ఇవ్వాలా?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ఆడియోను రికార్డ్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"మీరు యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే ఈ యాప్, ఆడియోను రికార్డ్ చేయగలుగుతుంది"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ఆడియోను రికార్డ్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"మీరు కాపీ చేసిన టెక్స్ట్, ఇమేజ్లను లేదా ఇతర కంటెంట్ను యాప్లు యాక్సెస్ చేసినప్పుడు మెసేజ్ను చూపుతుంది"</string> <string name="show_password_title" msgid="2877269286984684659">"పాస్వర్డ్లను చూపిస్తుంది"</string> <string name="show_password_summary" msgid="1110166488865981610">"మీరు టైప్ చేస్తున్నప్పుడు అక్షరాలను క్లుప్తంగా చూపిస్తుంది"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"ఈ యాప్, అది <xliff:g id="PERMISSION_NAME">%s</xliff:g> డేటాను థర్డ్-పార్టీలతో షేర్ చేయవచ్చని పేర్కొంది"</string> </resources> diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml index 3d1339249..e89a637fc 100644 --- a/PermissionController/res/values-th/strings.xml +++ b/PermissionController/res/values-th/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"คงไว้ที่ “เมื่อมีการใช้แอป”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"คงไว้ที่ “เฉพาะครั้งนี้”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ข้อมูลเพิ่มเติม"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ให้สิทธิ์เข้าถึงรูปภาพทั้งหมด"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"เลือกรูปภาพ"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"เลือกรูปภาพเพิ่มเติม"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"อย่าเลือกรูปภาพเพิ่มเติม"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ยังคงไม่อนุญาต"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ปิด"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> จาก <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> รายการ"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 วัน}other{# วัน}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 ชั่วโมง}other{# ชั่วโมง}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 นาที}other{# นาที}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 วินาที}other{# วินาที}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# วัน}other{# วัน}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ชั่วโมง}other{# ชั่วโมง}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# นาที}other{# นาที}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# วินาที}other{# วินาที}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"ทุกสิทธิ์"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"ทุกเวลา"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 วันที่ผ่านมา"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 ชั่วโมงที่ผ่านมา"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 ชั่วโมงที่ผ่านมา"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 นาทีที่ผ่านมา"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 นาทีที่ผ่านมา"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# วันที่ผ่านมา}other{# วันที่ผ่านมา}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# ชั่วโมงที่ผ่านมา}other{# ชั่วโมงที่ผ่านมา}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# นาทีที่ผ่านมา}other{# นาทีที่ผ่านมา}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"ไม่มีการใช้สิทธิ์"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"การเข้าถึงล่าสุดทั้งหมด"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"การเข้าถึงล่าสุดในช่วง 7 วันที่ผ่านมา"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"การใช้สิทธิ์ในช่วง 1 ชั่วโมงที่ผ่านมา"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"การใช้สิทธิ์ในช่วง 15 นาทีที่ผ่านมา"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"การใช้สิทธิ์ในช่วง 1 นาทีที่ผ่านมา"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"ไม่ได้ใช้ใน 24 ชั่วโมงที่ผ่านมา"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"ไม่ได้ใช้ใน 7 วันที่ผ่านมา"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{ไม่ได้ใช้ใน # วันที่ผ่านมา}other{ไม่ได้ใช้ใน # วันที่ผ่านมา}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{ไม่ได้ใช้ใน # ชั่วโมงที่ผ่านมา}other{ไม่ได้ใช้ใน # ชั่วโมงที่ผ่านมา}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{ใช้โดยแอป 1 แอป}other{ใช้โดยแอป # แอป}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"ดูทั้งหมดในหน้าแดชบอร์ด"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"กรองตาม: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"อนุญาตให้เข้าถึงสื่อเท่านั้น"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"อนุญาตตลอด"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"อนุญาตขณะมีการใช้แอปเท่านั้น"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"อนุญาตรูปภาพทั้งหมด"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"อนุญาตรูปภาพที่เลือก"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ถามทุกครั้ง"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"ไม่อนุญาต"</string> <string name="precise_image_description" msgid="6349638632303619872">"ตำแหน่งที่แน่นอน"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"ไม่อนุญาต"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ดูแอปอื่นๆ ที่สามารถเข้าถึงไฟล์ทั้งหมดได้"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 วัน}other{# วัน}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 ชั่วโมง}other{# ชั่วโมง}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 นาที}other{# นาที}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 วินาที}other{# วินาที}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ชั่วโมง}other{# ชั่วโมง}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# นาที}other{# นาที}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# วินาที}other{# วินาที}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"การช่วยเตือนเกี่ยวกับสิทธิ์"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"แอปที่ไม่ได้ใช้ 1 รายการ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"แอปที่ไม่ได้ใช้ <xliff:g id="NUMBER_OF_APPS">%s</xliff:g> รายการ"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึง<b>รูปภาพ วิดีโอ เพลง เสียง และไฟล์อื่นๆ</b> ในอุปกรณ์นี้ไหม"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงเพลงและเสียงในอุปกรณ์นี้ไหม"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงรูปภาพและวิดีโอในอุปกรณ์นี้ไหม"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ให้สิทธ์ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงรูปภาพเพิ่มเติมไหม"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> บันทึกเสียงไหม"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"แอปจะบันทึกเสียงได้ในขณะที่คุณใช้แอปอยู่เท่านั้น"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> บันทึกเสียงไหม"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"แสดงข้อความเมื่อแอปเข้าถึงข้อความ รูปภาพ หรือเนื้อหาอื่นๆ ที่คุณคัดลอก"</string> <string name="show_password_title" msgid="2877269286984684659">"แสดงรหัสผ่าน"</string> <string name="show_password_summary" msgid="1110166488865981610">"แสดงอักขระชั่วครู่ขณะพิมพ์"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"แอปนี้ระบุว่าแอปอาจแชร์ข้อมูล <xliff:g id="PERMISSION_NAME">%s</xliff:g> กับองค์กรบุคคลที่สามของแอป"</string> </resources> diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml index a22d0b276..ccc67fc34 100644 --- a/PermissionController/res/values-tl/strings.xml +++ b/PermissionController/res/values-tl/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Panatilihin ang “Habang ginagamit ang app”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Panatilihing “Sa pagkakataong ito lang”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Higit pang info"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Magbigay ng access sa lahat ng larawan"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pumili ng mga larawan"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pumili ng higit pang larawan"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Huwag pumili ng higit pang larawan"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Huwag pa ring payagan"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"I-dismiss"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sa <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 araw}one{# araw}other{# na araw}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 oras}one{# oras}other{# na oras}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 min}one{# min}other{# na min}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 segundo}one{# segundo}other{# na segundo}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# araw}one{# araw}other{# na araw}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# oras}one{# oras}other{# na oras}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# minuto}one{# minuto}other{# na minuto}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# segundo}one{# segundo}other{# na segundo}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Anumang pahintulot"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Anumang oras"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Nakalipas na 7 araw"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Nakalipas na 24 na oras"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Nakalipas na 1 oras"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Nakalipas na 15 minuto"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Nakalipas na 1 minuto"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Nakalipas na # araw}one{Nakalipas na # araw}other{Nakalipas na # na araw}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Nakalipas na # oras}one{Nakalipas na # oras}other{Nakalipas na # na oras}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Nakalipas na # minuto}one{Nakalipas na # minuto}other{Nakalipas na # na minuto}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Walang paggamit ng pahintulot"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Pinakakamakailang pag-access anumang oras"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Pinakakamakailang pag-access sa nakaraang 7 araw"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Paggamit ng pahintulot sa loob ng nakaraang 1 oras"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Paggamit ng pahintulot sa nakaraang 15 minuto"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Paggamit ng pahintulot sa nakaraang 1 minuto"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Hindi ginamit sa nakalipas na 24 na oras"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Hindi ginamit sa nakalipas na 7 araw"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Hindi ginamit sa nakalipas na # araw}one{Hindi ginamit sa nakalipas na # araw}other{Hindi ginamit sa nakalipas na # na araw}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Hindi ginamit sa nakalipas na # oras}one{Hindi ginamit sa nakalipas na # oras}other{Hindi ginamit sa nakalipas na # na oras}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Ginagamit ng 1 app}one{Ginagamit ng # app}other{Ginagamit ng # na app}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Tingnan lahat sa Dashboard"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Na-filter ng: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Payagan lang ang pag-access ng media"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Payagan sa lahat ng oras"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Payagan lang habang ginagamit ang app"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Payagan ang lahat ng larawan"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Payagan ang mga piniling larawan"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Magtanong palagi"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Huwag payagan"</string> <string name="precise_image_description" msgid="6349638632303619872">"Eksaktong lokasyon"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Hindi pinapayagan"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Tumingin pa ng mga app na puwedeng mag-access ng lahat ng file"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 araw}one{# araw}other{# na araw}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 oras}one{# oras}other{# na oras}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 minuto}one{# minuto}other{# na minuto}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 segundo}one{# segundo}other{# na segundo}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# oras}one{# oras}other{# na oras}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}one{# minuto}other{# na minuto}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}one{# segundo}other{# na segundo}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Mga paalala sa pahintulot"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 hindi ginagamit na app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> (na) hindi ginagamit na app"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang <b>mga larawan, video, musika, audio, at iba pang file</b> sa device?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang musika at audio sa device na ito?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang mga larawan at video sa device na ito?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Payagan ang access ng <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sa higit pang larawan?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na mag-record ng audio?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Makakapag-record lang ng audio ang app habang ginagamit mo ang app"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na mag-record ng audio?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Magpakita ng mensahe kapag ina-access ng mga app ang text, mga larawan, o iba pang content na nakopya mo"</string> <string name="show_password_title" msgid="2877269286984684659">"Ipakita ang mga password"</string> <string name="show_password_summary" msgid="1110166488865981610">"Ipakita sandali ang mga character habang nagta-type ka"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Isinaad ng app na ito na puwedeng ibahagi nito ang data ng <xliff:g id="PERMISSION_NAME">%s</xliff:g> sa mga third party"</string> </resources> diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml index b3223ea4b..927199165 100644 --- a/PermissionController/res/values-tr/strings.xml +++ b/PermissionController/res/values-tr/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Uygulama kullanılırken” tut"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Yalnızca bu defa\" sakla"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daha fazla bilgi"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Tüm fotoğraflara erişim izni ver"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotoğraf seç"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Daha fazla fotoğraf seç"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Daha fazla fotoğraf seçme"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Yine de izin verme"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Kapat"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 gün}other{# gün}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 saat}other{# saat}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 dk.}other{# dk.}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 sn.}other{# sn.}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# gün}other{# gün}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# saat}other{# saat}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# dk.}other{# dk.}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# sn.}other{# sn.}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Tüm izinler"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Tüm zamanlar"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Son 7 gün"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Son 24 saat"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Son 1 saat"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Son 15 dakika"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Son 1 dakika"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Son # gün}other{Son # gün}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Son # saat}other{Son # saat}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Son # dakika}other{Son # dakika}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"İzin kullanılmadı"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Herhangi bir zamanda gerçekleşen en son erişim"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Son 7 gün içindeki en son erişimler"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Son 1 saat içinde gerçekleşen izin kullanımı"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Son 15 dakika içinde gerçekleştirilen izin kullanımı"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Son 1 dakika içinde gerçekleşen izin kullanımı"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Son 24 saat içinde kullanılmadı"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Son 7 gün içinde kullanılmadı"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Son # gün içinde kullanılmadı}other{Son # gün içinde kullanılmadı}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Son # saat içinde kullanılmadı}other{Son # saat içinde kullanılmadı}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 uygulama tarafından kullanıldı}other{# uygulama tarafından kullanıldı}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Tümünü Kontrol Paneli\'nde göster"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtre ölçütü: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Yalnızca medyaya erişim izni ver"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Her zaman izin ver"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Yalnızca uygulama kullanılırken izin ver"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tüm fotoğraflara izin ver"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Seçilen fotoğraflara izin ver"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Her zaman sor"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"İzin verme"</string> <string name="precise_image_description" msgid="6349638632303619872">"Tam konum"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"İzin verilmeyenler"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Tüm dosyalara erişebilen diğer uygulamaları görün"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 gün}other{# gün}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 saat}other{# saat}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 dakika}other{# dakika}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 saniye}other{# saniye}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# saat}other{# saat}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# dakika}other{# dakika}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# saniye}other{# saniye}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"İzin hatırlatıcılar"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 kullanılmayan uygulama"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> kullanılmayan uygulama"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> için cihazdaki <b>fotoğraf, video, müzik, ses vb. dosyalara</b> erişim verilsin mi?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının bu cihazda müzik ve ses dosyalarına erişmesine izin verilsin mi?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının bu cihazdaki fotoğraf ve videolara erişmesine izin verilsin mi?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının daha fazla fotoğrafa erişmesine izin verilsin mi?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının ses kaydetmesine izin verilsin mi?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu uygulama, yalnızca kullanıldığı sırada ses kaydedebilir"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının ses kaydetmesine izin verilsin mi?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Uygulamalar kopyaladığınız metne, resimlere veya diğer içeriklere eriştiğinde mesaj gösterilsin."</string> <string name="show_password_title" msgid="2877269286984684659">"Şifreleri göster"</string> <string name="show_password_summary" msgid="1110166488865981610">"Yazarken karakterleri kısa süreliğine göster"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu uygulama, <xliff:g id="PERMISSION_NAME">%s</xliff:g> verilerini üçüncü taraflarla paylaşabileceğini belirtti"</string> </resources> diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml index dfefbe975..8a1b72f69 100644 --- a/PermissionController/res/values-uk/strings.xml +++ b/PermissionController/res/values-uk/strings.xml @@ -32,6 +32,14 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Дозволяти, лише коли додаток використовується"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Залишити дозвіл \"Лише цього разу\""</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Докладніше"</string> + <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) --> + <skip /> + <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) --> + <skip /> + <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) --> + <skip /> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Усе одно не дозволяти"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Закрити"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +145,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 день}one{# день}few{# дні}many{# днів}other{# дня}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 година}one{# година}few{# години}many{# годин}other{# години}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 хвилина}one{# хвилина}few{# хвилини}many{# хвилин}other{# хвилини}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 секунда}one{# секунда}few{# секунди}many{# секунд}other{# секунди}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# день}one{# день}few{# дні}many{# днів}other{# дня}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# година}one{# година}few{# години}many{# годин}other{# години}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# хв}one{# хв}few{# хв}many{# хв}other{# хв}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# с}one{# с}few{# с}many{# с}other{# с}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Будь-який дозвіл"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"У будь-який час"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Останні 7 днів"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Останні 24 години"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Остання година"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Останні 15 хвилин"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Остання хвилина"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{За останній # день}one{За останній # день}few{За останні # дні}many{За останні # днів}other{За останні # дня}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{За останню # годину}one{За останню # годину}few{За останні # години}many{За останні # годин}other{За останні # години}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{За останню # хвилину}one{За останню # хвилину}few{За останні # хвилини}many{За останні # хвилин}other{За останні # хвилини}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Дозволи не використовувалися"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Доступ за останній час"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Доступ за останні 7 днів"</string> @@ -161,8 +167,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Використання дозволів за останню годину"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Використання дозволів за останні 15 хвилин"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Використання дозволів за останню хвилину"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Дозвіл не використовувався протягом останніх 24 годин"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Дозвіл не використовувався протягом останніх 7 днів"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Не використовувався протягом останнього # дня}one{Не використовувався протягом останнього # дня}few{Не використовувався протягом останніх # днів}many{Не використовувався протягом останніх # днів}other{Не використовувався протягом останніх # дня}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Не використовувався протягом останньої # години}one{Не використовувався протягом останньої # години}few{Не використовувався протягом останніх # годин}many{Не використовувався протягом останніх # годин}other{Не використовувався протягом останніх # години}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Використано в 1 додатку}one{Використано в # додатку}few{Використано в # додатках}many{Використано в # додатках}other{Використано в # додатка}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Переглянути деталі на інформаційній панелі"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Відфільтровано за параметром \"<xliff:g id="PERM">%1$s</xliff:g>\""</string> @@ -188,6 +194,10 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Надати доступ лише до медіафайлів"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволяти завжди"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозволяти, лише коли додаток використовується"</string> + <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) --> + <skip /> + <!-- no translation found for app_permission_button_select_photos (1022930616634145364) --> + <skip /> <string name="app_permission_button_ask" msgid="3342950658789427">"Запитувати щоразу"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволяти"</string> <string name="precise_image_description" msgid="6349638632303619872">"Точне місцезнаходження"</string> @@ -253,9 +263,9 @@ <string name="denied_header" msgid="903209608358177654">"Заборонено"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Переглянути інші додатки, які мають доступ до всіх файлів"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 день}one{# день}few{# дні}many{# днів}other{# дня}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 година}one{# година}few{# години}many{# годин}other{# години}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 хвилина}one{# хвилина}few{# хвилини}many{# хвилин}other{# хвилини}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 секунда}one{# секунда}few{# секунди}many{# секунд}other{# секунди}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# година}one{# година}few{# години}many{# годин}other{# години}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# хвилина}one{# хвилина}few{# хвилини}many{# хвилин}other{# хвилини}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# секунда}one{# секунда}few{# секунди}many{# секунд}other{# секунди}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Нагадування про дозволи"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 невикористовуваний додаток"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Невикористовуваних додатків: <xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +480,8 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до <b>фото, відео, музики, аудіо й інших файлів</b> на пристрої?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до музики й аудіофайлів на цьому пристрої?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до фотографій і відео на цьому пристрої?"</string> + <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) --> + <skip /> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Дозволити додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записувати аудіо?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Додаток зможе записувати звук, лише коли ви використовуєте його"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Дозволити додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записувати звук?"</string> @@ -580,4 +592,6 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"З’являтиметься сповіщення, коли будь-який додаток отримуватиме доступ до скопійованого вами тексту, зображень чи іншого контенту"</string> <string name="show_password_title" msgid="2877269286984684659">"Показувати паролі"</string> <string name="show_password_summary" msgid="1110166488865981610">"Ненадовго показувати символи під час введення"</string> + <!-- no translation found for permission_rationale_message_template (4497650516269082051) --> + <skip /> </resources> diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml index a53ae34c8..9d73f11c7 100644 --- a/PermissionController/res/values-ur/strings.xml +++ b/PermissionController/res/values-ur/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"”جب تک ایپ استعمال میں ہے“ رکھیں"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"صرف اس وقت\" رکھیں"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"مزید معلومات"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"سبھی تصاویر تک رسائی کی اجازت دیں"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"تصاویر منتخب کریں"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"مزید تصاویر منتخب کریں"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"مزید تصاویر منتخب نہ کریں"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"کسی بھی صورت میں اجازت نہ دیں"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"برخاست کریں"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> میں سے <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 دن}other{# دن}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 گھنٹہ}other{# گھنٹے}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 منٹ}other{# منٹ}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 سیکنڈ}other{# سیکنڈ}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# دن}other{# دن}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# گھنٹہ}other{# گھنٹے}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# منٹ}other{# منٹ}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# سیکنڈ}other{# سیکنڈ}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"کوئی بھی اجازت"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"کسی بھی وقت"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"آخری 7 دن"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"آخری 24 گھنٹے"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"آخری 1 گھنٹہ"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"آخری 15 منٹ"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"آخری 1 منٹ"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{گزشتہ # دن}other{گزشتہ # دن}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{گزشتہ # گھنٹہ}other{گزشتہ # گھنٹے}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{گزشتہ # منٹ}other{گزشتہ # منٹ}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"اجازت کا استعمال نہیں ہوا ہے"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"کسی بھی وقت پر حالیہ ترین رسائی"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"آخری 7 دنوں میں حالیہ ترین رسائی"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"آخری 1 گھنٹے میں اجازت کا استعمال"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"آخری 15 منٹ میں اجازت کا استعمال"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"آخری 1 منٹ میں اجازت کا استعمال"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"گزشتہ 24 گھنٹے میں استعمال نہیں کی گئی"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"گزشتہ 7 دن میں استعمال نہیں کی گئی"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{گزشتہ # دن میں استعمال نہیں کی گئی}other{گزشتہ # دن میں استعمال نہیں کی گئی}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{گزشتہ # گھنٹے میں استعمال نہیں کی گئی}other{گزشتہ # گھنٹے میں استعمال نہیں کی گئی}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ایپ کے ذریعے استعمال کردہ}other{# ایپس کے ذریعے استعمال کردہ}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"سبھی کو ڈیش بورڈ میں دیکھیں"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"فلٹر کردہ بلحاظ: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"صرف میڈیا تک رسائی کی اجازت دیں"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ہر وقت اجازت دیں"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"صرف ایپ استعمال کرتے وقت اجازت دیں"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"تمام تصاویر کی اجازت دیں"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"منتخب کردہ تصاویر کی اجازت دیں"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"ہر بار پوچھیں"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"اجازت نہ دیں"</string> <string name="precise_image_description" msgid="6349638632303619872">"قطعی مقام"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"اجازت نہیں ہے"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"مزید ایپس دیکھیں جو تمام فائلز تک رسائی حاصل کر سکتی ہیں"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 دن}other{# دن}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 گھنٹہ}other{# گھنٹے}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 منٹ}other{# منٹ}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 سیکنڈ}other{# سیکنڈ}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# گھنٹہ}other{# گھنٹے}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# منٹ}other{# منٹ}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# سیکنڈ}other{# سیکنڈ}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"اجازت کی یاددہانیاں"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 غیر مستعمل ایپ"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> غیر مستعمل ایپس"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"اس آلے پر <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو <b>تصاویر، ویڈیوز، موسیقی، آڈیو اور دیگر فائلز</b> تک رسائی کی اجازت دیں؟"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"اس آلے پر <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو موسیقی اور آڈیو تک رسائی کی اجازت دیں؟"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"اس آلے پر <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"مزید تصاویر کے لیے <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو رسائی کی منظوری دیں؟"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"جب آپ ایپ استعمال کر رہے ہوں تب ایپ صرف آڈیو ریکارڈ کر پائے گی"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ایپس کے آپ کے کاپی کردہ ٹیکسٹ، تصاویر یا دیگر مواد تک رسائی حاصل کرنے پر پیغام دکھائیں"</string> <string name="show_password_title" msgid="2877269286984684659">"پاس ورڈز دکھائیں"</string> <string name="show_password_summary" msgid="1110166488865981610">"ٹائپ کرتے وقت حروف کو مختصر طور پر ڈسپلے کریں"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"اس ایپ نے بتایا ہے کہ یہ <xliff:g id="PERMISSION_NAME">%s</xliff:g> کے ڈیٹا کا اشتراک فریقین ثالث کے ساتھ کر سکتی ہے"</string> </resources> diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml index f084beaaf..34c1d372e 100644 --- a/PermissionController/res/values-uz/strings.xml +++ b/PermissionController/res/values-uz/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Ilova ishlatilganda” rejimida qolsin"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Faqat shu safar” ruxsat berish"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Batafsil"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Barcha suratlarga kirish uchun ruxsat berish"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Surat tanlash"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Yana surat tanlash"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Boshqa surat tanlanmasin"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Baribir rad etilsin"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Yopish"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 kun}other{# kun}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 soat}other{# soat}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 daq}other{# daq}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 son}other{# son}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# kun}other{# kun}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# soat}other{# soat}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# daq}other{# daq}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# son}other{# son}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Har qanday ruxsat"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Har qanday vaqt"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Oxirgi 7 kun"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Oxirgi 24 soat"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Oxirgi 1 soat"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Oxirgi 15 daqiqa"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Oxirgi 1 daqiqa"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Oxirgi # kun}other{Oxirgi # kun}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Oxirgi # soat}other{Oxirgi # soat}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Oxirgi # daqiqa}other{Oxirgi # daqiqa}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Ruxsatlardan foydalanilmagan"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Butun vaqtda ruxsatlardan foydalanish"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Oxirgi 7 kunda ruxsatlardan foydalanish"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Oxirgi 1 soatda ishlatilgan ruxsatlar"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Oxirgi 15 daqiqada ishlatilgan ruxsatlar"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Oxirgi 1 daqiqada ishlatilgan ruxsatlar"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Oxirgi 24 soatda foydalanilmagan"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Oxirgi 7 kunda foydalanilmagan"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Oxirgi # kunda foydalanilmagan}other{Oxirgi # kunda foydalanilmagan}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Oxirgi # soatda foydalanilmagan}other{Oxirgi # soatda foydalanilmagan}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ta ilova ishlatyapti}other{# ta ilova ishlatyapti}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Boshqaruv panelida ochish"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Filtrlar: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Faqat media fayllarga ruxsat"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Har doim ruxsat"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Faqat ilova faolligida ruxsat"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Barcha suratlarga kirish ruxsati"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tanlangan suratlarga kirish ruxsati"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Har safar soʻralsin"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Rad etish"</string> <string name="precise_image_description" msgid="6349638632303619872">"Aniq joylashuv"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Ruxsat berilmagan"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Barcha fayllarga kirish uchun koʻproq ilovalarni koʻring"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 kun}other{# kun}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 soat}other{# soat}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 daqiqa}other{# daqiqa}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 soniya}other{# soniya}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# soat}other{# soat}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# daqiqa}other{# daqiqa}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# soniya}other{# soniya}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Ruxsat eslatmalari"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ta ishlatilmagan ilova"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ta ishlatilmagan ilova"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi surat, video, musiqa, audio va fayllarga kirish ruxsati berilsinmi?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi musiqa va audio fayllarga kirish ruxsati berilsinmi?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun qurilmadagi rasm va videolarga kirish ruxsati berilsinmi?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga boshqa suratlarga kirish uchun ruxsat berilsinmi?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun audio yozib olish ruxsati berilsinmi?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu ilova faqat undan foydalanganingizda ovozlarni yozib oladi"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun audio yozib olishga ruxsat berilsinmi?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Ilovalar siz nusxa olgan matn, rasmlar yoki boshqa kontentdan foydalanganda xabar chiqarish"</string> <string name="show_password_title" msgid="2877269286984684659">"Parollar ochiq tursin"</string> <string name="show_password_summary" msgid="1110166488865981610">"Parolni kiritishda belgilar qisqa muddat ochiq turadi"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu ilovaning <xliff:g id="PERMISSION_NAME">%s</xliff:g> maʼlumotlarini begonalarga ulashishi mumkinligi xabar berildi"</string> </resources> diff --git a/PermissionController/res/values-v31/styles.xml b/PermissionController/res/values-v31/styles.xml index 64fc2900d..7ce3ff6e7 100644 --- a/PermissionController/res/values-v31/styles.xml +++ b/PermissionController/res/values-v31/styles.xml @@ -90,6 +90,14 @@ parent="@style/PermissionGrantButtonTop"></style> <style name="PermissionGrantButtonAllowOneTimeMaterial3" parent="@style/PermissionGrantButtonMiddle"></style> + <style name="PermissionGrantButtonAllowAllPhotosMaterial3" + parent="@style/PermissionGrantButtonTop"></style> + <style name="PermissionGrantButtonAllowSelectedPhotosMaterial3" + parent="@style/PermissionGrantButtonMiddle"></style> + <style name="PermissionGrantButtonAllowMorePhotosMaterial3" + parent="@style/PermissionGrantButtonTop"></style> + <style name="PermissionGrantButtonDontAllowMorePhotosMaterial3" + parent="@style/PermissionGrantButtonBottom"></style> <style name="PermissionGrantButtonDenyMaterial3" parent="@style/PermissionGrantButtonBottom"></style> <style name="PermissionGrantButtonNoUpgradeMaterial3" diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml index af1f23b98..376b08c2a 100644 --- a/PermissionController/res/values-v33/styles.xml +++ b/PermissionController/res/values-v33/styles.xml @@ -15,8 +15,7 @@ --> <resources - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> + xmlns:android="http://schemas.android.com/apk/res/android"> <!-- START SAFETY CENTER QUICK SETTINGS PAGE --> <style name="SafetyCenterQsContainer" @@ -174,9 +173,9 @@ <style name="SafetyCenterIndicatorCardView"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="app:cardCornerRadius">@dimen/sc_card_corner_radius_large</item> - <item name="app:cardElevation">0dp</item> - <item name="app:cardBackgroundColor">?attr/colorSurface</item> + <item name="cardCornerRadius">@dimen/sc_card_corner_radius_large</item> + <item name="cardElevation">0dp</item> + <item name="cardBackgroundColor">?attr/colorSurface</item> <item name="android:layout_marginTop">@dimen/sc_spacing_large</item> <item name="android:clickable">true</item> <item name="android:foreground">?android:attr/selectableItemBackground</item> @@ -342,9 +341,9 @@ <style name="SafetyCenterStatusImage" parent="android:Widget.DeviceDefault"> <item name="android:layout_width">56dp</item> <item name="android:layout_height">56dp</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">@id/status_title_and_summary</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">@id/status_title_and_summary</item> + <item name="layout_constraintStart_toStartOf">parent</item> <item name="android:scaleType">centerInside</item> <item name="android:gravity">center</item> </style> @@ -356,9 +355,9 @@ <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:layout_marginStart">@dimen/sc_spacing_large</item> <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item> - <item name="app:layout_constraintStart_toEndOf">@id/status_image</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/status_image</item> + <item name="layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> </style> <style name="SafetyCenterStatusTitle" parent="android:Widget.DeviceDefault"> @@ -370,7 +369,7 @@ <style name="SafetyCenterStatusSummary" parent="android:Widget.DeviceDefault"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/status_title</item> + <item name="layout_constraintTop_toBottomOf">@id/status_title</item> <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item> </style> @@ -379,39 +378,39 @@ <style name="SafetyCenterStatusButton.ReviewSettings"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/status_title_and_summary</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/status_title_and_summary</item> + <item name="layout_constraintStart_toStartOf">parent</item> <item name="android:layout_marginTop">@dimen/sc_spacing_xxxlarge</item> - <item name="app:backgroundTint">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/safety_center_button_info</item> </style> <style name="SafetyCenterStatusButton.Rescan"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/review_settings_button</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/review_settings_button</item> + <item name="layout_constraintStart_toStartOf">parent</item> <item name="android:layout_marginTop">@dimen/sc_spacing_xxxlarge</item> - <item name="app:backgroundTint">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/safety_center_button_info</item> </style> <style name="SafetyCenterStatusButton.PendingActionsRescan"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/review_settings_button</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/review_settings_button</item> + <item name="layout_constraintStart_toStartOf">parent</item> <item name="android:layout_marginTop">@dimen/sc_action_button_list_margin</item> - <item name="app:backgroundTint">@color/sc_surface_dark</item> - <item name="app:strokeWidth">@dimen/mtrl_btn_stroke_size</item> - <item name="app:strokeColor">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/sc_surface_dark</item> + <item name="strokeWidth">@dimen/mtrl_btn_stroke_size</item> + <item name="strokeColor">@color/safety_center_button_info</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="SafetyCenterStatusSafetyProtectionView"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/rescan_button</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/rescan_button</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> <item name="android:layout_gravity">center</item> <item name="android:paddingTop">@dimen/sc_spacing_xxlarge</item> <item name="android:paddingBottom">@dimen/sc_spacing_xxlarge</item> @@ -429,8 +428,19 @@ <item name="android:background">@android:color/transparent</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> + </style> + + <style name="SafetyCenterIssueAttributionTitle"> + <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item> + <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toStartOf">@id/issue_card_dismiss_btn</item> </style> <style name="SafetyCenterIssueTitle"> @@ -438,11 +448,11 @@ <item name="android:layout_width">0dp</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item> - <item name="app:layout_constraintHorizontal_bias">0</item> - <item name="app:layout_goneMarginEnd">0dp</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toStartOf">@id/issue_card_dismiss_btn</item> + <item name="layout_constraintHorizontal_bias">0</item> + <item name="layout_goneMarginEnd">0dp</item> + <item name="layout_constraintTop_toBottomOf">@id/issue_card_attribution_title</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toStartOf">@id/issue_card_dismiss_btn</item> </style> <style name="SafetyCenterIssueSubtitle"> @@ -450,8 +460,8 @@ <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">wrap_content</item> - <item name="app:layout_constraintStart_toStartOf">@id/issue_card_title</item> - <item name="app:layout_constraintTop_toBottomOf">@id/issue_card_title</item> + <item name="layout_constraintStart_toStartOf">@id/issue_card_title</item> + <item name="layout_constraintTop_toBottomOf">@id/issue_card_title</item> </style> <style name="SafetyCenterIssueSummary"> @@ -459,8 +469,8 @@ <item name="android:layout_marginTop">@dimen/sc_spacing_large</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">wrap_content</item> - <item name="app:layout_constraintStart_toStartOf">@id/issue_card_title</item> - <item name="app:layout_constraintTop_toBottomOf">@id/issue_card_subtitle</item> + <item name="layout_constraintStart_toStartOf">@id/issue_card_title</item> + <item name="layout_constraintTop_toBottomOf">@id/issue_card_subtitle</item> </style> <style name="SafetyCenterIssueActionButtonList" @@ -469,18 +479,18 @@ <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">0dp</item> <item name="android:orientation">vertical</item> - <item name="app:layout_constraintTop_toBottomOf">@id/issue_card_summary</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/issue_card_summary</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> </style> <style name="SafetyCenterIssueSafetyProtectionSection"> <item name="android:gravity">center</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">0dp</item> - <item name="app:layout_constraintTop_toBottomOf">@id/issue_card_action_button_list</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/issue_card_action_button_list</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> <item name="android:paddingTop">@dimen/sc_spacing_xxlarge</item> <item name="android:paddingBottom">@dimen/sc_spacing_xxlarge</item> </style> @@ -489,17 +499,17 @@ <item name="android:layout_width">0dp</item> <item name="android:layout_height">0dp</item> <item name="android:gravity">center</item> - <item name="app:layout_constraintWidth_default">wrap</item> - <item name="app:layout_constraintHeight_default">wrap</item> - <item name="app:layout_constraintWidth_max">112dp</item> - <item name="app:layout_constraintHeight_max">112dp</item> - <item name="app:layout_constraintWidth_min">84dp</item> - <item name="app:layout_constraintHeight_min">84dp</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toTopOf">@id/resolved_issue_text</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> - <item name="app:layout_constraintVertical_chainStyle">packed</item> + <item name="layout_constraintWidth_default">wrap</item> + <item name="layout_constraintHeight_default">wrap</item> + <item name="layout_constraintWidth_max">112dp</item> + <item name="layout_constraintHeight_max">112dp</item> + <item name="layout_constraintWidth_min">84dp</item> + <item name="layout_constraintHeight_min">84dp</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toTopOf">@id/resolved_issue_text</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintVertical_chainStyle">packed</item> </style> <style name="SafetyCenterIssueCardResolvedTitle"> @@ -511,10 +521,10 @@ <item name="android:gravity">center</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/resolved_issue_image</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/resolved_issue_image</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> </style> <style name="SafetyCenterMoreIssues" @@ -533,11 +543,11 @@ <item name="android:layout_marginEnd">@dimen/sc_spacing_xxlarge</item> <item name="android:maxLines">2</item> <item name="android:ellipsize">end</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> - <item name="app:layout_constraintStart_toEndOf">@id/status_icon</item> - <item name="app:layout_constraintEnd_toStartOf">@android:id/widget_frame</item> - <item name="app:layout_constraintHorizontal_bias">0</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/status_icon</item> + <item name="layout_constraintEnd_toStartOf">@android:id/widget_frame</item> + <item name="layout_constraintHorizontal_bias">0</item> </style> <style name="SafetyCenterMoreIssuesIcon" @@ -545,9 +555,9 @@ <item name="android:layout_height">20dp</item> <item name="android:layout_width">20dp</item> <item name="android:gravity">center</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintStart_toStartOf">parent</item> </style> <style name="SafetyCenterMoreIssuesWidgetFrame" @@ -556,9 +566,9 @@ <item name="android:layout_width">wrap_content</item> <item name="android:gravity">center_vertical</item> <item name="android:orientation">vertical</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> </style> <style name="SafetyCenterMoreIssuesWidget" @@ -578,10 +588,10 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxsmall</item> <item name="android:maxLines">1</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> - <item name="app:layout_constraintEnd_toStartOf">@id/widget_icon</item> - <item name="app:layout_constraintHorizontal_bias">0</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintEnd_toStartOf">@id/widget_icon</item> + <item name="layout_constraintHorizontal_bias">0</item> </style> <style name="SafetyCenterMoreIssuesWidgetIcon" @@ -589,9 +599,9 @@ <item name="android:layout_height">16dp</item> <item name="android:layout_width">16dp</item> <item name="android:gravity">center</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintBottom_toBottomOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintBottom_toBottomOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> <item name="android:scaleType">fitCenter</item> <item name="android:tint">?android:attr/textColorPrimary</item> </style> diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml index ab0340562..42e59aa00 100644 --- a/PermissionController/res/values-vi/strings.xml +++ b/PermissionController/res/values-vi/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Duy trì tùy chọn “Khi đang dùng ứng dụng”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Duy trì tùy chọn “Chỉ lần này”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Thông tin khác"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Cho phép truy cập vào toàn bộ ảnh"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Chọn ảnh"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Chọn thêm ảnh"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Không chọn thêm ảnh"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Vẫn không cho phép"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Đóng"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 ngày}other{# ngày}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 giờ}other{# giờ}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 phút}other{# phút}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 giây}other{# giây}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# ngày}other{# ngày}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# giờ}other{# giờ}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# phút}other{# phút}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# giây}other{# giây}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Mọi quyền"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Mọi lúc"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"7 ngày qua"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"24 giờ qua"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"1 giờ qua"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"15 phút qua"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"1 phút qua"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{# ngày qua}other{# ngày qua}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{# giờ qua}other{# giờ qua}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{# phút qua}other{# phút qua}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Không sử dụng quyền"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Lần truy cập gần đây nhất vào bất kỳ thời gian nào"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Lần truy cập gần đây nhất trong 7 ngày qua"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Tần suất sử dụng quyền trong 1 giờ trước"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Tần suất sử dụng quyền trong 15 phút trước"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Tần suất sử dụng quyền trong 1 phút trước"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Không sử dụng trong 24 giờ qua"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Không sử dụng trong 7 ngày qua"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Không sử dụng trong # ngày qua}other{Không sử dụng trong # ngày qua}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Không sử dụng trong # giờ qua}other{Không sử dụng trong # giờ qua}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 ứng dụng đã dùng}other{# ứng dụng đã dùng}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Xem tất cả trong Trang tổng quan"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Lọc theo: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Chỉ cho phép truy cập vào nội dung nghe nhìn"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Luôn cho phép"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Chỉ cho phép khi dùng ứng dụng"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Cho phép truy cập vào toàn bộ ảnh"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Cho phép truy cập vào ảnh đã chọn"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Luôn hỏi"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Không cho phép"</string> <string name="precise_image_description" msgid="6349638632303619872">"Vị trí chính xác"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Không được phép"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Xem thêm ứng dụng có thể truy cập tất cả các tệp"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ngày}other{# ngày}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 giờ}other{# giờ}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 phút}other{# phút}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 giây}other{# giây}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# giờ}other{# giờ}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# phút}other{# phút}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# giây}other{# giây}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Lời nhắc về quyền"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 ứng dụng không dùng đến"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ứng dụng không dùng đến"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào <b>ảnh, video, nhạc, âm thanh và các tệp khác</b> trên thiết bị này?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào nhạc và âm thanh trên thiết bị này?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào ảnh và video trên thiết bị này?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Bạn có đồng ý cấp quyền truy cập thêm ảnh cho <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ghi âm?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Ứng dụng này chỉ có thể ghi âm khi bạn đang dùng ứng dụng"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ghi âm?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Hiện thông báo khi có ứng dụng truy cập vào văn bản, hình ảnh hoặc nội dung khác mà bạn đã sao chép"</string> <string name="show_password_title" msgid="2877269286984684659">"Hiển thị mật khẩu"</string> <string name="show_password_summary" msgid="1110166488865981610">"Hiển thị các ký tự ngắn gọn khi bạn nhập"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Nhà phát triển nêu rõ ứng dụng này có thể chia sẻ dữ liệu <xliff:g id="PERMISSION_NAME">%s</xliff:g> với bên thứ ba"</string> </resources> diff --git a/PermissionController/res/values-w764dp-v33/styles.xml b/PermissionController/res/values-w764dp-v33/styles.xml index a443f4e26..02ed655ad 100644 --- a/PermissionController/res/values-w764dp-v33/styles.xml +++ b/PermissionController/res/values-w764dp-v33/styles.xml @@ -26,56 +26,56 @@ <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:layout_marginStart">@dimen/sc_spacing_large</item> <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item> - <item name="app:layout_constraintStart_toEndOf">@id/status_image</item> - <item name="app:layout_constraintEnd_toStartOf">@id/review_settings_button</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/status_image</item> + <item name="layout_constraintEnd_toStartOf">@id/review_settings_button</item> + <item name="layout_constraintTop_toTopOf">parent</item> </style> <style name="SafetyCenterStatusButton.ReviewSettings"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintStart_toEndOf">@id/status_title_and_summary</item> - <item name="app:layout_constraintEnd_toStartOf">@id/rescan_button</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/status_title_and_summary</item> + <item name="layout_constraintEnd_toStartOf">@id/rescan_button</item> <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item> <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item> - <item name="app:backgroundTint">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/safety_center_button_info</item> </style> <style name="SafetyCenterStatusButton.Rescan"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintStart_toEndOf">@id/review_settings_button</item> - <item name="app:layout_constraintEnd_toStartOf">@id/pending_actions_rescan_button</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/review_settings_button</item> + <item name="layout_constraintEnd_toStartOf">@id/pending_actions_rescan_button</item> <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item> <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item> - <item name="app:backgroundTint">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/safety_center_button_info</item> </style> <style name="SafetyCenterStatusButton.PendingActionsRescan"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toTopOf">parent</item> - <item name="app:layout_constraintStart_toEndOf">@id/rescan_button</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toTopOf">parent</item> + <item name="layout_constraintStart_toEndOf">@id/rescan_button</item> + <item name="layout_constraintEnd_toEndOf">parent</item> <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item> <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item> <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item> - <item name="app:backgroundTint">@color/sc_surface_dark</item> - <item name="app:strokeWidth">@dimen/mtrl_btn_stroke_size</item> - <item name="app:strokeColor">@color/safety_center_button_info</item> + <item name="backgroundTint">@color/sc_surface_dark</item> + <item name="strokeWidth">@dimen/mtrl_btn_stroke_size</item> + <item name="strokeColor">@color/safety_center_button_info</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="SafetyCenterStatusSafetyProtectionView"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="app:layout_constraintTop_toBottomOf">@id/status_title_and_summary</item> - <item name="app:layout_constraintStart_toStartOf">parent</item> - <item name="app:layout_constraintEnd_toEndOf">parent</item> + <item name="layout_constraintTop_toBottomOf">@id/status_title_and_summary</item> + <item name="layout_constraintStart_toStartOf">parent</item> + <item name="layout_constraintEnd_toEndOf">parent</item> <item name="android:layout_gravity">center</item> <item name="android:paddingTop">@dimen/sc_spacing_xxlarge</item> <item name="android:paddingBottom">@dimen/sc_spacing_xxlarge</item> diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml index 08467a438..7e142a9d6 100644 --- a/PermissionController/res/values-zh-rCN/strings.xml +++ b/PermissionController/res/values-zh-rCN/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"保留“在使用该应用期间”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"继续使用“仅限这一次”设置"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多信息"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允许访问所有照片"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"选择照片"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"选择更多照片"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不选择更多照片"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"仍然不允许"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"关闭"</string> <string name="current_permission_template" msgid="7452035392573329375">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 项权限(共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 项)"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 小时}other{# 小时}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 分钟}other{# 分钟}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# 天}other{# 天}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# 小时}other{# 小时}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# 分钟}other{# 分钟}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"不限权限"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"不限时间"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"过去 7 天"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"过去 24 小时"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"过去 1 小时"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"过去 15 分钟"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"过去 1 分钟"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{过去 # 天}other{过去 # 天}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{过去 # 小时}other{过去 # 小时}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{过去 # 分钟}other{过去 # 分钟}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"没有使用此权限的应用"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"最近使用的访问权限(不限时间)"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"最近使用的访问权限(过去 7 天内)"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"过去 1 小时内的权限使用频率"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"过去 15 分钟内的权限使用频率"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"过去 1 分钟内的权限使用频率"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"过去 24 小时内未使用"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"过去 7 天内未使用过"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{过去 # 天内未使用过}other{过去 # 天内未使用过}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{过去 # 小时内未使用过}other{过去 # 小时内未使用过}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{有 1 个应用使用过}other{有 # 个应用使用过}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"在信息中心查看全部详细信息"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"过滤条件:<xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"仅允许访问媒体文件"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"始终允许"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"仅在使用该应用时允许"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允许访问所有照片"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允许访问所选照片"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"每次都询问"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"不允许"</string> <string name="precise_image_description" msgid="6349638632303619872">"确切位置"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"不允许"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"查看更多可以访问所有文件的应用"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 小时}other{# 小时}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 分钟}other{# 分钟}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小时}other{# 小时}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# 分钟}other{# 分钟}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"权限提醒"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 个未使用的应用"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> 个未使用的应用"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允许“<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>”访问此设备上的<b>照片、视频、音乐、音频和其他文件</b>吗?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问此设备上的音乐和音频吗?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问此设备上的照片和视频吗?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"要授予<xliff:g id="APP_NAME">%1$s</xliff:g>访问更多照片的权限吗?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"要允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>录音吗?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此应用将只能在您使用它时录音"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"要允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>录音吗?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系统会在应用访问您复制的文字、图片或其他内容时显示一条消息"</string> <string name="show_password_title" msgid="2877269286984684659">"显示密码"</string> <string name="show_password_summary" msgid="1110166488865981610">"输入时短暂显示字符"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"此应用已声明它可能会与第三方共享<xliff:g id="PERMISSION_NAME">%s</xliff:g>数据"</string> </resources> diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml index c353200a7..e44f78516 100644 --- a/PermissionController/res/values-zh-rHK/strings.xml +++ b/PermissionController/res/values-zh-rHK/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"保持為「使用應用程式時」"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"保留「只在這次允許」"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資料"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允許存取所有相片"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"選取相片"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"選取更多相片"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不選取更多相片"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"一律不允許"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"關閉"</string> <string name="current_permission_template" msgid="7452035392573329375">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 個 (共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 個)"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 小時}other{# 小時}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 分鐘}other{# 分鐘}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# 天}other{# 天}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# 小時}other{# 小時}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"任何權限"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"不限時間"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"過去 7 天"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"過去 24 小時"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"過去 1 小時"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"過去 15 分鐘"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"過去 1 分鐘"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{過去 # 日}other{過去 # 日}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{過去 # 小時}other{過去 # 小時}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{過去 # 分鐘}other{過去 # 分鐘}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"沒有應用程式使用要求的權限"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"任意時段內的最近一次存取"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"過去 7 天內的近期存取"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"過去 1 小時內的權限使用情況"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"過去 15 分鐘內的權限使用情況"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"過去 1 分鐘內的權限使用情況"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"過去 24 小時內未使用"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"過去 7 天內未使用"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{過去 # 天內並未使用}other{過去 # 天內並未使用}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{過去 # 小時內並未使用}other{過去 # 小時內並未使用}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{有 1 個應用程式使用過}other{有 # 個應用程式使用過}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"在「資訊主頁」查看全部"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"篩選條件:<xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"僅允許存取媒體"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"一律允許"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"僅在使用此應用程式時允許"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允許存取所有相片"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允許存取所選相片"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"每次都詢問"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"不允許"</string> <string name="precise_image_description" msgid="6349638632303619872">"精確位置"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"不允許"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"顯示更多可存取所有檔案的應用程式"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 小時}other{# 小時}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 分鐘}other{# 分鐘}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 秒}other{# 秒}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小時}other{# 小時}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# 秒}other{# 秒}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"權限提醒"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 個不使用的應用程式"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> 個不使用的應用程式"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許 <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 存取此裝置上的<b>相片、影片、音樂、音訊和其他檔案</b>嗎?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許 <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 存取此裝置上的音樂和音訊嗎?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允許 <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 存取此裝置上的相片和影片嗎?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取更多相片嗎?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>錄音嗎?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此應用程式將只能在您使用期間錄音"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>錄音嗎?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系統會在應用程式存取您複製的文字、圖片或其他內容時顯示訊息"</string> <string name="show_password_title" msgid="2877269286984684659">"顯示密碼"</string> <string name="show_password_summary" msgid="1110166488865981610">"輸入時短暫顯示字元"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"此應用程式表明可能會與第三方分享<xliff:g id="PERMISSION_NAME">%s</xliff:g>資料"</string> </resources> diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml index 69328e0ca..8dd663927 100644 --- a/PermissionController/res/values-zh-rTW/strings.xml +++ b/PermissionController/res/values-zh-rTW/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"不要變更「應用程式使用期間」"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"保留「僅允許這一次」"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資訊"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允許存取所有相片"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"選取相片"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"選取更多相片"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不要選取更多相片"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"仍不允許"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"關閉"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{1 小時}other{# 小時}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{1 分鐘}other{# 分鐘}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{1 秒鐘}other{# 秒鐘}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{# 天}other{# 天}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# 小時}other{# 小時}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{# 秒鐘}other{# 秒鐘}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"不限權限"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"不限時間"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"過去 7 天"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"過去 24 小時"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"過去 1 小時"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"過去 15 分鐘"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"過去 1 分鐘內"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{過去 # 天內}other{過去 # 天內}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{過去 # 小時內}other{過去 # 小時內}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{過去 # 分鐘內}other{過去 # 分鐘內}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"沒有使用此權限的應用程式"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"近期使用的存取權 (依時間順序)"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"過去 7 天內使用的存取權 (依時間順序)"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"過去 1 小時內各權限的使用頻率"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"過去 15 分鐘內各權限的使用頻率"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"過去 1 分鐘內各權限的使用頻率"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"過去 24 小時內未使用"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"過去 7 天內未使用"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{過去 # 天內未使用}other{過去 # 天內未使用}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{過去 # 小時內未使用}other{過去 # 小時內未使用}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{1 個應用程式使用過}other{# 個應用程式使用過}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"在資訊主頁查看所有詳細資料"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"篩選依據:<xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"只允許存取媒體檔案"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"一律允許"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"僅在使用該應用程式時允許"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允許所有相片"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允許選取的相片"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"每次都詢問"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"不允許"</string> <string name="precise_image_description" msgid="6349638632303619872">"精確位置"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"不允許"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"查看可存取所有檔案的其他應用程式"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{1 小時}other{# 小時}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{1 分鐘}other{# 分鐘}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{1 秒鐘}other{# 秒鐘}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小時}other{# 小時}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# 秒鐘}other{# 秒鐘}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"權限提醒"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 個未使用的應用程式"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> 個未使用的應用程式"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取這部裝置上的<b>相片、影片、音樂、音訊和其他檔案</b>嗎?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取這部裝置上的音樂和音訊嗎?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取這部裝置上的相片和影片嗎?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取更多相片嗎?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"這個應用程式只有在你使用時才能錄音"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系統會在應用程式存取你複製的文字、圖片或其他內容時顯示通知訊息"</string> <string name="show_password_title" msgid="2877269286984684659">"顯示密碼"</string> <string name="show_password_summary" msgid="1110166488865981610">"輸入密碼時,短暫顯示剛輸入的字元"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"這個應用程式表示可能會將「<xliff:g id="PERMISSION_NAME">%s</xliff:g>」資料分享給第三方"</string> </resources> diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml index 092f5a05d..b5afa9c16 100644 --- a/PermissionController/res/values-zu/strings.xml +++ b/PermissionController/res/values-zu/strings.xml @@ -32,6 +32,10 @@ <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Gcina okuthi “Ngenkathi uhlelo lokusebenza lusebenza”"</string> <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Gcina “Kulesi sikhathi kuphela”"</string> <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Olunye ulwazi"</string> + <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Vumela Ukufinyelela kuzo zonke izithombe"</string> + <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Khetha izithombe"</string> + <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Khetha izithombe eziningi"</string> + <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ungakhethi izithombe eziningi"</string> <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ungavumeli noma kunjalo"</string> <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Vula"</string> <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kokungu-<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> @@ -137,17 +141,15 @@ <string name="auto_permission_usage_timeline_summary" msgid="2713135806453218703">"<xliff:g id="ACCESS_TIME">%1$s</xliff:g> • <xliff:g id="SUMMARY_TEXT">%2$s</xliff:g>"</string> <string name="history_preference_subtext_2" msgid="1521763591164293683">"<xliff:g id="APP_NAME">%1$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%2$s</xliff:g>"</string> <string name="history_preference_subtext_3" msgid="758761785983094351">"<xliff:g id="ATTRIBUTION_NAME">%1$s</xliff:g> • <xliff:g id="APP_NAME">%2$s</xliff:g> • <xliff:g id="TRUNCATED_TIME">%3$s</xliff:g>"</string> - <string name="duration_used_days" msgid="8293010131040301793">"{count,plural, =1{usuku 1}one{izinsuku #}other{izinsuku #}}"</string> - <string name="duration_used_hours" msgid="1128716208752263576">"{count,plural, =1{ihora 1}one{amahora #}other{amahora #}}"</string> - <string name="duration_used_minutes" msgid="5335824115042576567">"{count,plural, =1{umzuzu 1}one{imizuzu #}other{imizuzu #}}"</string> - <string name="duration_used_seconds" msgid="6543746449171675028">"{count,plural, =1{umzuzwana 1}one{imizuzwana #}other{imizuzwana #}}"</string> + <string name="duration_used_days" msgid="8238355545812998877">"{count,plural, =1{Usuku olungu-#}one{Izinsuku ezingu-#}other{Izinsuku ezingu-#}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{Ihora elingu-#}one{Amahora angu-#}other{Amahora angu-#}}"</string> + <string name="duration_used_minutes" msgid="1701379522897227819">"{count,plural, =1{Umzuzu ongu-#}one{Imizuzu engu-#}other{Imizuzu engu-#}}"</string> + <string name="duration_used_seconds" msgid="4067390990568727715">"{count,plural, =1{Isekhondi elingu-#}one{Amasekhondi angu-#}other{Amasekhondi angu-#}}"</string> <string name="permission_usage_any_permission" msgid="6358023078298106997">"Noma iyiphi imvume"</string> <string name="permission_usage_any_time" msgid="3802087027301631827">"Noma yisiphi isikhathi"</string> - <string name="permission_usage_last_7_days" msgid="7386221251886130065">"Izinsuku zokugcina ezingu-7"</string> - <string name="permission_usage_last_day" msgid="1512880889737305115">"Amahora angu-24 okugcina"</string> - <string name="permission_usage_last_hour" msgid="3866005205535400264">"Ihora lokugcina elingu-1"</string> - <string name="permission_usage_last_15_minutes" msgid="9077554653436200702">"Amaminithi angu-15 okugcina"</string> - <string name="permission_usage_last_minute" msgid="7297055967335176238">"Iminithi lokugcina elingu-1"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Usuku lokugcina olungu-#}one{Izinsuku zokugcina ezingu-#}other{Izinsuku zokugcina ezingu-#}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Ihora lokugcina elingu-#}one{Amahora okugcina angu-#}other{Amahora okugcina angu-#}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Umzuzu wokugcina ongu-#}one{Imizuzu yokugcina engu-#}other{Imizuzu yokugcina engu-#}}"</string> <string name="no_permission_usages" msgid="9119517454177289331">"Akukho ukusetshenziswa kwemvume"</string> <string name="permission_usage_list_title_any_time" msgid="8718257027381592407">"Ukufinyelela kwakamuva kakhulu noma kunini"</string> <string name="permission_usage_list_title_last_7_days" msgid="9048542342670890615">"Ukufinyelela kwakamuva kakhulu ezinsukwini zokugcina ezingu-7"</string> @@ -161,8 +163,8 @@ <string name="permission_usage_bar_chart_title_last_hour" msgid="6571647509660009185">"Ukusetshenziswa kwembume kuhora lokugcina elingu-1"</string> <string name="permission_usage_bar_chart_title_last_15_minutes" msgid="2743143675412824819">"Ukusetshenziswa kwemvume kumaminithi okugcina okungu-15"</string> <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Ukusetshenziswa kwemvume ngeminithi elingu-1 lokugcina"</string> - <string name="permission_usage_preference_summary_not_used_24h" msgid="3087783232178611025">"Ayisetshenziswanga emahoreni angama-24 adlule"</string> - <string name="permission_usage_preference_summary_not_used_7d" msgid="4592301300810120096">"Akusetshenziswanga ezinsukwini ezi-7 ezedlule"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Akusetshenziswanga osukwini olungu-# olwedlule}one{Akusetshenziswanga ezinsukwini ezingu-# ezedlule}other{Akusetshenziswanga ezinsukwini ezingu-# ezedlule}}"</string> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours" msgid="3828973177433435742">"{count,plural, =1{Akusetshenziswanga ehoreni elingu-# eledlule}one{Akusetshenziswanga emahoreni angu-# adlule}other{Akusetshenziswanga emahoreni angu-# adlule}}"</string> <string name="permission_usage_preference_label" msgid="8343167938128676378">"{count,plural, =1{Kusetshenziswe i-app e-1}one{Kusetshenziswe ama-app angu-#}other{Kusetshenziswe ama-app angu-#}}"</string> <string name="permission_usage_view_details" msgid="6675335735468752787">"Bona konke kudeshibhodi"</string> <string name="app_permission_usage_filter_label" msgid="7182861154638631550">"Kuhlungwe ngalokhu: <xliff:g id="PERM">%1$s</xliff:g>"</string> @@ -188,6 +190,8 @@ <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Vumela ukufinyelela kumidiya kuphela"</string> <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vumela sonke isikhathi"</string> <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Vumela kuphela ngenkathi usebenzisa uhlelo lokusebenza"</string> + <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Vumela zonke izithombe"</string> + <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Vumela izithombe ezikhethiwe"</string> <string name="app_permission_button_ask" msgid="3342950658789427">"Buza njalo"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Ungavumeli"</string> <string name="precise_image_description" msgid="6349638632303619872">"Indawo eqondile"</string> @@ -253,9 +257,9 @@ <string name="denied_header" msgid="903209608358177654">"Akuvumelekile"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bona ama-app engeziwe akwazi ukufinyelela wonke amafayela"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{usuku 1}one{izinsuku #}other{izinsuku #}}"</string> - <string name="hours" msgid="3447767892295843282">"{count,plural, =1{ihora 1}one{amahora #}other{amahora #}}"</string> - <string name="minutes" msgid="4408293038068503157">"{count,plural, =1{umzuzu 1}one{imizuzu #}other{imizuzu #}}"</string> - <string name="seconds" msgid="5397771912131132690">"{count,plural, =1{umzuzwana 1}one{imizuzwana #}other{imizuzwana #}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{Ihora elingu-#}one{Amahora angu-#}other{Amahora angu-#}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{Umzuzu ongu-#}one{Imizuzu engu-#}other{Imizuzu engu-#}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{Isekhondi engu-#}one{Amasekhondi angu-#}other{Amasekhondi angu-#}}"</string> <string name="permission_reminders" msgid="6528257957664832636">"Izikhumbuzi zemvume"</string> <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"Uhlelo lokusebenza olungasetshenzisiwe olu-1"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"Izinhlelo zokusebenza ezingasetshenzisiwe ezingu-<xliff:g id="NUMBER_OF_APPS">%s</xliff:g>"</string> @@ -470,6 +474,7 @@ <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe, amavidiyo, umculo, okulalelwayo, namanye amafayela kule divayisi?"</string> <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele umculo nokulalelwayo kule divayisi?"</string> <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe namavidiyo kule divayisi?"</string> + <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Nikeza i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukufinyelela izithombe ezengeziwe?"</string> <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi irekhode umsindo?"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Uhlelo lokusebenza luzokwazi ukurekhoda imisindo kuphela kuyilapho usebenzisa uhlelo lokusebenza"</string> <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi irekhode umsindo?"</string> @@ -580,4 +585,5 @@ <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Bonisa umlayezo uma ama-app wakho afinyelela umbhalo, izithombe, noma okunye okuqukethwe okukopishile"</string> <string name="show_password_title" msgid="2877269286984684659">"Bonisa amaphasiwedi"</string> <string name="show_password_summary" msgid="1110166488865981610">"Bonisa izinhlamvu kancane njengoba uthayipha"</string> + <string name="permission_rationale_message_template" msgid="4497650516269082051">"Le-app ithi ingabelana ngedatha ye-<xliff:g id="PERMISSION_NAME">%s</xliff:g> nezinkampani zangaphandle"</string> </resources> diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml index 6e7973fb1..ed8dc8969 100644 --- a/PermissionController/res/values/overlayable.xml +++ b/PermissionController/res/values/overlayable.xml @@ -61,6 +61,12 @@ <item type="style" name="PermissionGrantButtonAllowOneTime" /> <item type="style" name="PermissionGrantButtonDeny" /> <item type="style" name="PermissionGrantButtonNoUpgrade" /> + + <item type="style" name="PermissionGrantPermissionRationaleContent" /> + <item type="style" name="PermissionGrantPermissionRationaleIcon" /> + <item type="style" name="PermissionGrantPermissionRationaleMessage" /> + <item type="style" name="PermissionGrantPermissionRationaleMoreInfoIcon" /> + <!-- END PERMISSION GRANT DIALOG --> diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml index 287f81865..a660341d7 100644 --- a/PermissionController/res/values/strings.xml +++ b/PermissionController/res/values/strings.xml @@ -67,6 +67,18 @@ <!-- Title for the dialog button to get more info about a permission. [CHAR LIMIT=15] --> <string name="grant_dialog_button_more_info">More info</string> + <!-- Title for the dialog button to allow access to all photos. [CHAR LIMIT=60] --> + <string name="grant_dialog_button_allow_all_photos">Allow access to all photos</string> + + <!-- Title for the dialog button to allow access to select photos to be shared. [CHAR LIMIT=60] --> + <string name="grant_dialog_button_allow_selected_photos">Select photos</string> + + <!-- Title for the dialog button to allow access to select more photos to be shared. [CHAR LIMIT=60] --> + <string name="grant_dialog_button_allow_more_selected_photos">Select more photos</string> + + <!-- Title for the dialog button to not allow access to select more photos to be shared. [CHAR LIMIT=60] --> + <string name="grant_dialog_button_dont_allow_more_selected_photos">Don\u2019t select more photos</string> + <!-- Title for the dialog button to deny a permission grant despite a warning of implications. [CHAR LIMIT=30] --> <string name="grant_dialog_button_deny_anyway">Don\u2019t allow anyway</string> @@ -412,25 +424,25 @@ <!-- Duration used for a permission in days --> <string name="duration_used_days">{count, plural, - =1 {1 day} + =1 {# day} other {# days} }</string> <!-- Duration used for a permission in hours --> <string name="duration_used_hours">{count, plural, - =1 {1 hour} + =1 {# hour} other {# hours} }</string> <!-- Duration used for a permission in minutes --> <string name="duration_used_minutes">{count, plural, - =1 {1 min} + =1 {# min} other {# mins} }</string> <!-- Duration used for a permission in seconds --> <string name="duration_used_seconds">{count, plural, - =1 {1 sec} + =1 {# sec} other {# secs} }</string> @@ -440,20 +452,23 @@ <!-- Description for showing permission accesses accessed any time [CHAR LIMIT=30] --> <string name="permission_usage_any_time">Any time</string> - <!-- Description for showing permissions accessed in the last 7 days [CHAR LIMIT=30] --> - <string name="permission_usage_last_7_days">Last 7 days</string> - - <!-- Description for showing permissions accessed in the last day [CHAR LIMIT=30] --> - <string name="permission_usage_last_day">Last 24 hours</string> - - <!-- Description for showing permissions accessed in the last hour [CHAR LIMIT=30] --> - <string name="permission_usage_last_hour">Last 1 hour</string> + <!-- Description for showing permissions accessed in the last n days [CHAR LIMIT=30] --> + <string name="permission_usage_last_n_days">{count, plural, + =1 {Last # day} + other {Last # days} + }</string> - <!-- Description for showing permissions accessed in the last 15 minutes [CHAR LIMIT=30] --> - <string name="permission_usage_last_15_minutes">Last 15 minutes</string> + <!-- Description for showing permissions accessed in the last n hours [CHAR LIMIT=30] --> + <string name="permission_usage_last_n_hours">{count, plural, + =1 {Last # hour} + other {Last # hours} + }</string> - <!-- Description for showing permissions accessed in the last minute [CHAR LIMIT=30] --> - <string name="permission_usage_last_minute">Last 1 minute</string> + <!-- Description for showing permissions accessed in the last n minutes [CHAR LIMIT=30] --> + <string name="permission_usage_last_n_minutes">{count, plural, + =1 {Last # minute} + other {Last # minutes} + }</string> <!-- Label when no apps have used the requested permissions [CHAR LIMIT=30] --> <string name="no_permission_usages">No permission usages</string> @@ -494,11 +509,17 @@ <!-- Label for the title of the permission bar chart showing how often the most common permissions are used [CHAR LIMIT=50] --> <string name="permission_usage_bar_chart_title_last_minute">Permission usage in last 1 minute</string> - <!-- Summary text if a permission usage is not used in past 24 hours [CHAR LIMIT=60] --> - <string name="permission_usage_preference_summary_not_used_24h">Not used in past 24 hours</string> + <!-- Summary text if a permission usage is not used in past n days [CHAR LIMIT=60] --> + <string name="permission_usage_preference_summary_not_used_in_past_n_days">{count, plural, + =1 {Not used in past # day} + other {Not used in past # days} + }</string> - <!-- Summary text if a permission usage is not used in past 7 days [CHAR LIMIT=60] --> - <string name="permission_usage_preference_summary_not_used_7d">Not used in past 7 days</string> + <!-- Summary text if a permission usage is not used in past n hours [CHAR LIMIT=60] --> + <string name="permission_usage_preference_summary_not_used_in_past_n_hours">{count, plural, + =1 {Not used in past # hour} + other {Not used in past # hours} + }</string> <!-- Label for the permission usage preference that shows how many apps have used various permissions [CHAR LIMIT=50] --> <string name="permission_usage_preference_label">{count, plural, @@ -584,6 +605,12 @@ <!-- Title for the dialog button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=60] --> <string name="app_permission_button_allow_foreground">Allow only while using the app</string> + <!-- Title for the dialog button to allow the user to allow all photos. [CHAR LIMIT=60] --> + <string name="app_permission_button_allow_all_photos">Allow all photos</string> + + <!-- Title for the dialog button to allow the user to select photos. [CHAR LIMIT=60] --> + <string name="app_permission_button_select_photos">Allow selected photos</string> + <!-- Title for the dialog button to require an app to ask for a permission next time they need it. [CHAR LIMIT=60] --> <string name="app_permission_button_ask">Ask every time</string> @@ -784,19 +811,19 @@ <!-- Time in hours --> <string name="hours">{count, plural, - =1 {1 hour} + =1 {# hour} other {# hours} }</string> <!-- Time in minutes --> <string name="minutes">{count, plural, - =1 {1 minute} + =1 {# minute} other {# minutes} }</string> <!-- Time in seconds --> <string name="seconds">{count, plural, - =1 {1 second} + =1 {# second} other {# seconds} }</string> @@ -1182,9 +1209,17 @@ <!-- Description for the watch profile role. [CHAR LIMIT=NONE] --> <string name="role_watch_description"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string> + <!-- TODO(b/261147998): STOPSHIP update with finalized description string --> + <!-- Description for the glasses profile role. [CHAR LIMIT=NONE] --> + <string name="role_companion_device_glasses_description"><xliff:g id="app_name" example="GlassesApp">%1$s</xliff:g> will be allowed to access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string> + <!-- Description for the app streaming profile role. [CHAR LIMIT=NONE] --> <string name="role_app_streaming_description"><xliff:g id="app_name" example="Cross-Device Communciation">%1$s</xliff:g> will be allowed to interact with your notifications and stream your apps to the connected device.</string> + <!-- TODO(b/261147998): STOPSHIP update with finalized description string --> + <!-- Description for the nearby device streaming profile role. [CHAR LIMIT=NONE] --> + <string name="role_companion_device_nearby_device_streaming_description"><xliff:g id="app_name" example="NearbyStreamer">%1$s</xliff:g> will be allowed to stream content to nearby devices.</string> + <!-- Description for the companion device computer profile role. [CHAR LIMIT=NONE] --> <string name="role_companion_device_computer_description">This service shares your photos, media, and notifications from your phone to other devices.</string> @@ -1425,6 +1460,10 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <string name="permgrouprequest_read_media_visual">Allow <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access photos and videos on this device?</string> <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] --> + <string name="permgrouprequest_more_photos">Grant + <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> access to more photos?</string> + + <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] --> <string name="permgrouprequest_microphone">Allow <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to record audio?</string> <!-- Subtitle of the message shown to the user when the apps requests permission to use the microphone only while app is in foreground [CHAR LIMIT=150]--> @@ -1682,4 +1721,13 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!-- Summary for toggle controlling whether to show the first letter while typing passwords. [CHAR LIMIT=NONE] --> <string name="show_password_summary">Display characters briefly as you type</string> + <!-- TODO(b/259279178): update with finalized permission rationale strings --> + <!-- Template for the permission rationale message when an app requests a permission. Third + parties are other organizations outside of the app developer. These could be companies or even + governmental organizations. But because we aren't able to be inclusive of all possibilities, + phrasing should be as generic as possible while still helping users understand they aren't just + sharing data with the developer company. [CHAR LIMIT=100] --> + <string name="permission_rationale_message_template">This app stated it may share + <xliff:g id="permission_name" example="location">%s</xliff:g> data with third parties</string> + </resources> diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml index 3826a853d..60f39874b 100644 --- a/PermissionController/res/values/styles.xml +++ b/PermissionController/res/values/styles.xml @@ -104,13 +104,20 @@ <item name="android:orientation">horizontal</item> </style> + <style name="PermissionLocationAccuracyRadioGroupMaterial3"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginBottom">24dp</item> + <item name="android:gravity">center_horizontal</item> + <item name="android:orientation">horizontal</item> + </style> + <style name="PermissionLocationAccuracyRadioFine"> <item name="android:button">@null</item> <item name="android:background">@null</item> <item name="android:gravity">center_horizontal</item> <item name="android:layout_marginEnd">16dp</item> <item name="android:drawablePadding">8dp</item> - <item name="android:gravity">center_horizontal</item> </style> <style name="PermissionLocationAccuracyRadioCoarse"> @@ -119,7 +126,6 @@ <item name="android:gravity">center_horizontal</item> <item name="android:layout_marginStart">16dp</item> <item name="android:drawablePadding">8dp</item> - <item name="android:gravity">center_horizontal</item> </style> <style name="PermissionLocationAccuracyFineImageView"> @@ -129,6 +135,12 @@ <item name="android:layout_marginBottom">24dp</item> </style> + <style name="PermissionLocationAccuracyFineImageViewMaterial3"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginBottom">24dp</item> + </style> + <style name="PermissionLocationAccuracyCoarseImageView"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> @@ -136,6 +148,12 @@ <item name="android:layout_marginBottom">24dp</item> </style> + <style name="PermissionLocationAccuracyCoarseImageViewMaterial3"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginBottom">24dp</item> + </style> + <style name="PermissionGrantButtonList"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> @@ -165,6 +183,44 @@ <item name="android:background">?android:attr/selectableItemBackground</item> </style> + <style name="PermissionGrantPermissionRationaleContent"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginStart">24dp</item> + <item name="android:layout_marginEnd">24dp</item> + <item name="android:layout_marginBottom">24dp</item> + <item name="android:padding">12dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@drawable/grant_dialog_permission_rationale_background</item> + </style> + + <style name="PermissionGrantPermissionRationaleIcon"> + <item name="android:layout_width">20dp</item> + <item name="android:layout_height">20dp</item> + <item name="android:layout_gravity">start|center_vertical</item> + <item name="android:scaleType">fitCenter</item> + <item name="android:tint">?android:attr/textColorSecondary</item> + </style> + + <style name="PermissionGrantPermissionRationaleMessage" + parent="@android:style/TextAppearance.DeviceDefault"> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_weight">1</item> + <item name="android:layout_marginStart">12dp</item> + <item name="android:layout_marginEnd">12dp</item> + <item name="android:textSize">14sp</item> + </style> + + <style name="PermissionGrantPermissionRationaleMoreInfoIcon"> + <item name="android:layout_width">20dp</item> + <item name="android:layout_height">20dp</item> + <item name="android:layout_gravity">end|center_vertical</item> + <item name="android:scaleType">fitCenter</item> + <item name="android:tint">?android:attr/textColorSecondary</item> + </style> + <!-- for use in overlays --> <style name="PermissionGrantButtonAllow" parent="@style/PermissionGrantButton"></style> @@ -172,6 +228,14 @@ parent="@style/PermissionGrantButton"></style> <style name="PermissionGrantButtonAllowOneTime" parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowAllPhotos" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowMorePhotos" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowSelectedPhotos" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonDontAllowMorePhotos" + parent="@style/PermissionGrantButton"></style> <style name="PermissionGrantButtonDeny" parent="@style/PermissionGrantButton"></style> <style name="PermissionGrantButtonNoUpgrade" @@ -187,6 +251,14 @@ parent="@style/PermissionGrantButton"></style> <style name="PermissionGrantButtonNoUpgradeMaterial3" parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowAllPhotosMaterial3" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowSelectedPhotosMaterial3" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonAllowMorePhotosMaterial3" + parent="@style/PermissionGrantButton"></style> + <style name="PermissionGrantButtonDontAllowMorePhotosMaterial3" + parent="@style/PermissionGrantButton"></style> <!-- END PERMISSION GRANT DIALOG --> @@ -1106,18 +1178,16 @@ <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> </style> - <style name="WarningBannerCardView" - xmlns:card_view="http://schemas.android.com/apk/res-auto" - xmlns:app="http://schemas.android.com/apk/res-auto" > + <style name="WarningBannerCardView"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="app:cardCornerRadius">20dp</item> - <item name="app:cardBackgroundColor">@color/warning_surface</item> - <item name="app:cardElevation">0dp</item> - <item name="card_view:contentPaddingBottom">8dp</item> - <item name="card_view:contentPaddingTop">20dp</item> - <item name="card_view:contentPaddingLeft">20dp</item> - <item name="card_view:contentPaddingRight">20dp</item> + <item name="cardCornerRadius">20dp</item> + <item name="cardBackgroundColor">@color/warning_surface</item> + <item name="cardElevation">0dp</item> + <item name="contentPaddingBottom">8dp</item> + <item name="contentPaddingTop">20dp</item> + <item name="contentPaddingLeft">20dp</item> + <item name="contentPaddingRight">20dp</item> </style> <style name="WarningBannerIcon"> diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml index 16ff3c15a..76196a050 100644 --- a/PermissionController/res/values/themes.xml +++ b/PermissionController/res/values/themes.xml @@ -57,8 +57,12 @@ <item name="android:windowIsTranslucent">true</item> </style> - <style name="GrantPermissions.Car"> - <item name="carUiActivity">true</item> + <style name="GrantPermissions.Car" parent="Theme.CarUi.NoToolbar"> + <item name="android:windowNoTitle">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <!-- The following attributes change the behavior of the dialog, hence they should not be + themed --> + <item name="android:windowIsTranslucent">true</item> </style> <!-- Unused since R but exposed as overlayable. --> @@ -101,7 +105,7 @@ <item name="android:background">@color/divider_color_primary</item> </style> - <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" /> + <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" /> <!-- Do not allow OEMs to overlay these themes. diff --git a/PermissionController/res/xml-v34/safety_center_subpage.xml b/PermissionController/res/xml-v34/safety_center_subpage.xml new file mode 100644 index 000000000..75089e40d --- /dev/null +++ b/PermissionController/res/xml-v34/safety_center_subpage.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + +</PreferenceScreen> diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 1a37f3538..744115250 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -84,6 +84,7 @@ <permission name="android.permission.BLUETOOTH_ADVERTISE" minSdkVersion="31" /> <permission name="android.permission.BLUETOOTH_CONNECT" minSdkVersion="31" /> <permission name="android.permission.BLUETOOTH_SCAN" minSdkVersion="31" /> + <permission name="android.permission.NEARBY_WIFI_DEVICES" minSdkVersion="33" /> </permission-set> <permission-set name="notifications"> @@ -101,7 +102,8 @@ label="@string/role_assistant_label" overrideUserWhenGranting="true" requestable="false" - shortLabel="@string/role_assistant_short_label"> + shortLabel="@string/role_assistant_short_label" + uiBehavior="AssistantRoleUiBehavior"> <required-components> <!-- Qualified components are determined int AssistantRoleBehavior. This comment here is ignored and represents just a rough description @@ -159,7 +161,8 @@ overrideUserWhenGranting="true" requestDescription="@string/role_browser_request_description" requestTitle="@string/role_browser_request_title" - shortLabel="@string/role_browser_short_label"> + shortLabel="@string/role_browser_short_label" + uiBehavior="BrowserRoleUiBehavior"> <!-- ~ Required components matching is handled in BrowserRoleBehavior because it needs the ~ PackageManager.MATCH_ALL flag and other manual filtering, which cannot fit in our @@ -201,7 +204,8 @@ requestDescription="@string/role_dialer_request_description" requestTitle="@string/role_dialer_request_title" searchKeywords="@string/role_dialer_search_keywords" - shortLabel="@string/role_dialer_short_label"> + shortLabel="@string/role_dialer_short_label" + uiBehavior="DialerRoleUiBehavior"> <required-components> <activity> <intent-filter> @@ -287,7 +291,8 @@ requestDescription="@string/role_sms_request_description" requestTitle="@string/role_sms_request_title" searchKeywords="@string/role_sms_search_keywords" - shortLabel="@string/role_sms_short_label"> + shortLabel="@string/role_sms_short_label" + uiBehavior="SmsRoleUiBehavior"> <required-components> <receiver permission="android.permission.BROADCAST_SMS"> <intent-filter> @@ -377,7 +382,8 @@ requestTitle="@string/role_emergency_request_title" searchKeywords="@string/role_emergency_search_keywords" shortLabel="@string/role_emergency_short_label" - systemOnly="true"> + systemOnly="true" + uiBehavior="EmergencyRoleUiBehavior"> <required-components> <activity> <intent-filter> @@ -407,7 +413,8 @@ requestDescription="@string/role_home_request_description" requestTitle="@string/role_home_request_title" searchKeywords="@string/role_home_search_keywords" - shortLabel="@string/role_home_short_label"> + shortLabel="@string/role_home_short_label" + uiBehavior="HomeRoleUiBehavior"> <!-- Also used by HomeRoleBehavior.getFallbackHolder(). --> <required-components> <activity> @@ -600,6 +607,7 @@ <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" minSdkVersion="33" /> <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" minSdkVersion="33" /> + <permission name="android.permission.MANAGE_DEVICE_LOCK_STATE" minSdkVersion="34" /> </permissions> </role> @@ -1108,6 +1116,38 @@ </permissions> </role> + <role + name="android.app.role.COMPANION_DEVICE_GLASSES" + description="@string/role_companion_device_glasses_description" + exclusive="false" + minSdkVersion="34" + systemOnly="false" + visible="false"> + <permissions> + <permission-set name="contacts" /> + <permission-set name="microphone" /> + <permission-set name="nearby_devices" /> + <permission-set name="phone" /> + <permission-set name="sms" /> + </permissions> + </role> + + <role + name="android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING" + allowBypassingQualification="true" + description="@string/role_companion_device_nearby_device_streaming_description" + exclusive="false" + minSdkVersion="34" + systemOnly="true" + visible="false"> + <permissions> + <permission-set name="nearby_devices" /> + <permission name="android.permission.CREATE_VIRTUAL_DEVICE" /> + <permission name="android.permission.ADD_TRUSTED_DISPLAY" /> + <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" /> + </permissions> + </role> + <role name="android.app.role.SYSTEM_SUPERVISION" defaultHolders="config_systemSupervision" @@ -1187,6 +1227,7 @@ <permission name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" /> <permission name="android.permission.QUERY_ADMIN_POLICY" /> <permission name="android.permission.TRIGGER_LOST_MODE" /> + <permission name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS" /> </permissions> </role> @@ -1340,4 +1381,17 @@ <permission name="android.permission.NET_TUNNELING" /> </permissions> </role> + + <!-- + ~ A role assigned to the financing kiosk app + --> + <role + name="android.app.role.FINANCED_DEVICE_KIOSK" + exclusive="true" + minSdkVersion="34" + visible="false"> + <permissions> + <permission name="android.permission.MANAGE_DEVICE_LOCK_STATE" /> + </permissions> + </role> </roles> diff --git a/PermissionController/src/android/support/wearable/view/CircledImageView.java b/PermissionController/src/android/support/wearable/view/CircledImageView.java deleted file mode 100644 index 915ba691e..000000000 --- a/PermissionController/src/android/support/wearable/view/CircledImageView.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.wear.ble.view; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.RadialGradient; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.AttributeSet; -import android.view.View; - -import com.android.permissioncontroller.R; - -import java.util.Objects; - -/** - * An image view surrounded by a circle. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class CircledImageView extends View { - - private static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator(); - - private Drawable mDrawable; - - private final RectF mOval; - private final Paint mPaint; - - private ColorStateList mCircleColor; - - private float mCircleRadius; - private float mCircleRadiusPercent; - - private float mCircleRadiusPressed; - private float mCircleRadiusPressedPercent; - - private float mRadiusInset; - - private int mCircleBorderColor; - - private float mCircleBorderWidth; - private float mProgress = 1f; - private final float mShadowWidth; - - private float mShadowVisibility; - private boolean mCircleHidden = false; - - private float mInitialCircleRadius; - - private boolean mPressed = false; - - private boolean mProgressIndeterminate; - private ProgressDrawable mIndeterminateDrawable; - private Rect mIndeterminateBounds = new Rect(); - private long mColorChangeAnimationDurationMs = 0; - - private float mImageCirclePercentage = 1f; - private float mImageHorizontalOffcenterPercentage = 0f; - private Integer mImageTint; - - private final Drawable.Callback mDrawableCallback = new Drawable.Callback() { - @Override - public void invalidateDrawable(Drawable drawable) { - invalidate(); - } - - @Override - public void scheduleDrawable(Drawable drawable, Runnable runnable, long l) { - // Not needed. - } - - @Override - public void unscheduleDrawable(Drawable drawable, Runnable runnable) { - // Not needed. - } - }; - - private int mCurrentColor; - - private final AnimatorUpdateListener mAnimationListener = new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - int color = (int) animation.getAnimatedValue(); - if (color != CircledImageView.this.mCurrentColor) { - CircledImageView.this.mCurrentColor = color; - CircledImageView.this.invalidate(); - } - } - }; - - private ValueAnimator mColorAnimator; - - public CircledImageView(Context context) { - this(context, null); - } - - public CircledImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public CircledImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircledImageView); - mDrawable = a.getDrawable(R.styleable.CircledImageView_android_src); - - mCircleColor = a.getColorStateList(R.styleable.CircledImageView_circle_color); - if (mCircleColor == null) { - mCircleColor = ColorStateList.valueOf(android.R.color.darker_gray); - } - - mCircleRadius = a.getDimension( - R.styleable.CircledImageView_circle_radius, 0); - mInitialCircleRadius = mCircleRadius; - mCircleRadiusPressed = a.getDimension( - R.styleable.CircledImageView_circle_radius_pressed, mCircleRadius); - mCircleBorderColor = a.getColor( - R.styleable.CircledImageView_circle_border_color, Color.BLACK); - mCircleBorderWidth = a.getDimension(R.styleable.CircledImageView_circle_border_width, 0); - - if (mCircleBorderWidth > 0) { - mRadiusInset += mCircleBorderWidth; - } - - float circlePadding = a.getDimension(R.styleable.CircledImageView_circle_padding, 0); - if (circlePadding > 0) { - mRadiusInset += circlePadding; - } - mShadowWidth = a.getDimension(R.styleable.CircledImageView_shadow_width, 0); - - mImageCirclePercentage = a.getFloat( - R.styleable.CircledImageView_image_circle_percentage, 0f); - - mImageHorizontalOffcenterPercentage = a.getFloat( - R.styleable.CircledImageView_image_horizontal_offcenter_percentage, 0f); - - if (a.hasValue(R.styleable.CircledImageView_image_tint)) { - mImageTint = a.getColor(R.styleable.CircledImageView_image_tint, 0); - } - - mCircleRadiusPercent = a.getFraction(R.styleable.CircledImageView_circle_radius_percent, - 1, 1, 0f); - - mCircleRadiusPressedPercent = a.getFraction( - R.styleable.CircledImageView_circle_radius_pressed_percent, 1, 1, - mCircleRadiusPercent); - - a.recycle(); - - mOval = new RectF(); - mPaint = new Paint(); - mPaint.setAntiAlias(true); - - mIndeterminateDrawable = new ProgressDrawable(); - // {@link #mDrawableCallback} must be retained as a member, as Drawable callback - // is held by weak reference, we must retain it for it to continue to be called. - mIndeterminateDrawable.setCallback(mDrawableCallback); - - setWillNotDraw(false); - - setColorForCurrentState(); - } - - public void setCircleHidden(boolean circleHidden) { - if (circleHidden != mCircleHidden) { - mCircleHidden = circleHidden; - invalidate(); - } - } - - - @Override - protected boolean onSetAlpha(int alpha) { - return true; - } - - @Override - protected void onDraw(Canvas canvas) { - int paddingLeft = getPaddingLeft(); - int paddingTop = getPaddingTop(); - - - float circleRadius = mPressed ? getCircleRadiusPressed() : getCircleRadius(); - if (mShadowWidth > 0 && mShadowVisibility > 0) { - // First let's find the center of the view. - mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(), - getHeight() - getPaddingBottom()); - // Having the center, lets make the shadow start beyond the circled and possibly the - // border. - final float radius = circleRadius + mCircleBorderWidth + - mShadowWidth * mShadowVisibility; - mPaint.setColor(Color.BLACK); - mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha())); - mPaint.setStyle(Style.FILL); - // TODO: precalc and pre-allocate this - mPaint.setShader(new RadialGradient(mOval.centerX(), mOval.centerY(), radius, - new int[]{Color.BLACK, Color.TRANSPARENT}, new float[]{0.6f, 1f}, - Shader.TileMode.MIRROR)); - canvas.drawCircle(mOval.centerX(), mOval.centerY(), radius, mPaint); - mPaint.setShader(null); - } - if (mCircleBorderWidth > 0) { - // First let's find the center of the view. - mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(), - getHeight() - getPaddingBottom()); - // Having the center, lets make the border meet the circle. - mOval.set(mOval.centerX() - circleRadius, mOval.centerY() - circleRadius, - mOval.centerX() + circleRadius, mOval.centerY() + circleRadius); - mPaint.setColor(mCircleBorderColor); - // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the - // color. {@link #Paint.setPaint} will clear any previously set alpha value. - mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha())); - mPaint.setStyle(Style.STROKE); - mPaint.setStrokeWidth(mCircleBorderWidth); - - if (mProgressIndeterminate) { - mOval.roundOut(mIndeterminateBounds); - mIndeterminateDrawable.setBounds(mIndeterminateBounds); - mIndeterminateDrawable.setRingColor(mCircleBorderColor); - mIndeterminateDrawable.setRingWidth(mCircleBorderWidth); - mIndeterminateDrawable.draw(canvas); - } else { - canvas.drawArc(mOval, -90, 360 * mProgress, false, mPaint); - } - } - if (!mCircleHidden) { - mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(), - getHeight() - getPaddingBottom()); - // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the - // color. {@link #Paint.setPaint} will clear any previously set alpha value. - mPaint.setColor(mCurrentColor); - mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha())); - - mPaint.setStyle(Style.FILL); - float centerX = mOval.centerX(); - float centerY = mOval.centerY(); - - canvas.drawCircle(centerX, centerY, circleRadius, mPaint); - } - - if (mDrawable != null) { - mDrawable.setAlpha(Math.round(getAlpha() * 255)); - - if (mImageTint != null) { - mDrawable.setTint(mImageTint); - } - mDrawable.draw(canvas); - } - - super.onDraw(canvas); - } - - private void setColorForCurrentState() { - int newColor = mCircleColor.getColorForState(getDrawableState(), - mCircleColor.getDefaultColor()); - if (mColorChangeAnimationDurationMs > 0) { - if (mColorAnimator != null) { - mColorAnimator.cancel(); - } else { - mColorAnimator = new ValueAnimator(); - } - mColorAnimator.setIntValues(new int[] { - mCurrentColor, newColor }); - mColorAnimator.setEvaluator(ARGB_EVALUATOR); - mColorAnimator.setDuration(mColorChangeAnimationDurationMs); - mColorAnimator.addUpdateListener(this.mAnimationListener); - mColorAnimator.start(); - } else { - if (newColor != mCurrentColor) { - mCurrentColor = newColor; - invalidate(); - } - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - - final float radius = getCircleRadius() + mCircleBorderWidth + - mShadowWidth * mShadowVisibility; - float desiredWidth = radius * 2; - float desiredHeight = radius * 2; - - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - int width; - int height; - - if (widthMode == MeasureSpec.EXACTLY) { - width = widthSize; - } else if (widthMode == MeasureSpec.AT_MOST) { - width = (int) Math.min(desiredWidth, widthSize); - } else { - width = (int) desiredWidth; - } - - if (heightMode == MeasureSpec.EXACTLY) { - height = heightSize; - } else if (heightMode == MeasureSpec.AT_MOST) { - height = (int) Math.min(desiredHeight, heightSize); - } else { - height = (int) desiredHeight; - } - - super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (mDrawable != null) { - // Retrieve the sizes of the drawable and the view. - final int nativeDrawableWidth = mDrawable.getIntrinsicWidth(); - final int nativeDrawableHeight = mDrawable.getIntrinsicHeight(); - final int viewWidth = getMeasuredWidth(); - final int viewHeight = getMeasuredHeight(); - final float imageCirclePercentage = mImageCirclePercentage > 0 - ? mImageCirclePercentage : 1; - - final float scaleFactor = Math.min(1f, - Math.min( - (float) nativeDrawableWidth != 0 - ? imageCirclePercentage * viewWidth / nativeDrawableWidth : 1, - (float) nativeDrawableHeight != 0 - ? imageCirclePercentage - * viewHeight / nativeDrawableHeight : 1)); - - // Scale the drawable down to fit the view, if needed. - final int drawableWidth = Math.round(scaleFactor * nativeDrawableWidth); - final int drawableHeight = Math.round(scaleFactor * nativeDrawableHeight); - - // Center the drawable within the view. - final int drawableLeft = (viewWidth - drawableWidth) / 2 - + Math.round(mImageHorizontalOffcenterPercentage * drawableWidth); - final int drawableTop = (viewHeight - drawableHeight) / 2; - - mDrawable.setBounds(drawableLeft, drawableTop, drawableLeft + drawableWidth, - drawableTop + drawableHeight); - } - - super.onLayout(changed, left, top, right, bottom); - } - - public void setImageDrawable(Drawable drawable) { - if (drawable != mDrawable) { - final Drawable existingDrawable = mDrawable; - mDrawable = drawable; - - final boolean skipLayout = drawable != null - && existingDrawable != null - && existingDrawable.getIntrinsicHeight() == drawable.getIntrinsicHeight() - && existingDrawable.getIntrinsicWidth() == drawable.getIntrinsicWidth(); - - if (skipLayout) { - mDrawable.setBounds(existingDrawable.getBounds()); - } else { - requestLayout(); - } - - invalidate(); - } - } - - public void setImageResource(int resId) { - setImageDrawable(resId == 0 ? null : getContext().getDrawable(resId)); - } - - public void setImageCirclePercentage(float percentage) { - float clamped = Math.max(0, Math.min(1, percentage)); - if (clamped != mImageCirclePercentage) { - mImageCirclePercentage = clamped; - invalidate(); - } - } - - public void setImageHorizontalOffcenterPercentage(float percentage) { - if (percentage != mImageHorizontalOffcenterPercentage) { - mImageHorizontalOffcenterPercentage = percentage; - invalidate(); - } - } - - public void setImageTint(int tint) { - if (tint != mImageTint) { - mImageTint = tint; - invalidate(); - } - } - - public float getCircleRadius() { - float radius = mCircleRadius; - if (mCircleRadius <= 0 && mCircleRadiusPercent > 0) { - radius = Math.max(getMeasuredHeight(), getMeasuredWidth()) * mCircleRadiusPercent; - } - - return radius - mRadiusInset; - } - - public float getCircleRadiusPercent() { - return mCircleRadiusPercent; - } - - public float getCircleRadiusPressed() { - float radius = mCircleRadiusPressed; - - if (mCircleRadiusPressed <= 0 && mCircleRadiusPressedPercent > 0) { - radius = Math.max(getMeasuredHeight(), getMeasuredWidth()) - * mCircleRadiusPressedPercent; - } - - return radius - mRadiusInset; - } - - public float getCircleRadiusPressedPercent() { - return mCircleRadiusPressedPercent; - } - - public void setCircleRadius(float circleRadius) { - if (circleRadius != mCircleRadius) { - mCircleRadius = circleRadius; - invalidate(); - } - } - - /** - * Sets the radius of the circle to be a percentage of the largest dimension of the view. - * @param circleRadiusPercent A {@code float} from 0 to 1 representing the radius percentage. - */ - public void setCircleRadiusPercent(float circleRadiusPercent) { - if (circleRadiusPercent != mCircleRadiusPercent) { - mCircleRadiusPercent = circleRadiusPercent; - invalidate(); - } - } - - public void setCircleRadiusPressed(float circleRadiusPressed) { - if (circleRadiusPressed != mCircleRadiusPressed) { - mCircleRadiusPressed = circleRadiusPressed; - invalidate(); - } - } - - /** - * Sets the radius of the circle to be a percentage of the largest dimension of the view when - * pressed. - * @param circleRadiusPressedPercent A {@code float} from 0 to 1 representing the radius - * percentage. - */ - public void setCircleRadiusPressedPercent(float circleRadiusPressedPercent) { - if (circleRadiusPressedPercent != mCircleRadiusPressedPercent) { - mCircleRadiusPressedPercent = circleRadiusPressedPercent; - invalidate(); - } - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - setColorForCurrentState(); - } - - public void setCircleColor(int circleColor) { - setCircleColorStateList(ColorStateList.valueOf(circleColor)); - } - - public void setCircleColorStateList(ColorStateList circleColor) { - if (!Objects.equals(circleColor, mCircleColor)) { - mCircleColor = circleColor; - setColorForCurrentState(); - invalidate(); - } - } - - public ColorStateList getCircleColorStateList() { - return mCircleColor; - } - - public int getDefaultCircleColor() { - return mCircleColor.getDefaultColor(); - } - - /** - * Show the circle border as an indeterminate progress spinner. - * The views circle border width and color must be set for this to have an effect. - * - * @param show true if the progress spinner is shown, false to hide it. - */ - public void showIndeterminateProgress(boolean show) { - mProgressIndeterminate = show; - if (show) { - mIndeterminateDrawable.startAnimation(); - } else { - mIndeterminateDrawable.stopAnimation(); - } - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - if (visibility != View.VISIBLE) { - showIndeterminateProgress(false); - } else if (mProgressIndeterminate) { - showIndeterminateProgress(true); - } - } - - public void setProgress(float progress) { - if (progress != mProgress) { - mProgress = progress; - invalidate(); - } - } - - /** - * Set how much of the shadow should be shown. - * @param shadowVisibility Value between 0 and 1. - */ - public void setShadowVisibility(float shadowVisibility) { - if (shadowVisibility != mShadowVisibility) { - mShadowVisibility = shadowVisibility; - invalidate(); - } - } - - public float getInitialCircleRadius() { - return mInitialCircleRadius; - } - - public void setCircleBorderColor(int circleBorderColor) { - mCircleBorderColor = circleBorderColor; - } - - /** - * Set the border around the circle. - * @param circleBorderWidth Width of the border around the circle. - */ - public void setCircleBorderWidth(float circleBorderWidth) { - if (circleBorderWidth != mCircleBorderWidth) { - mCircleBorderWidth = circleBorderWidth; - invalidate(); - } - } - - @Override - public void setPressed(boolean pressed) { - super.setPressed(pressed); - if (pressed != mPressed) { - mPressed = pressed; - invalidate(); - } - } - - public Drawable getImageDrawable() { - return mDrawable; - } - - /** - * @return the milliseconds duration of the transition animation when the color changes. - */ - public long getColorChangeAnimationDuration() { - return mColorChangeAnimationDurationMs; - } - - /** - * @param mColorChangeAnimationDurationMs the milliseconds duration of the color change - * animation. The color change animation will run if the color changes with {@link #setCircleColor} - * or as a result of the active state changing. - */ - public void setColorChangeAnimationDuration(long mColorChangeAnimationDurationMs) { - this.mColorChangeAnimationDurationMs = mColorChangeAnimationDurationMs; - } -} diff --git a/PermissionController/src/android/support/wearable/view/Gusterpolator.java b/PermissionController/src/android/support/wearable/view/Gusterpolator.java deleted file mode 100644 index 6b17100fe..000000000 --- a/PermissionController/src/android/support/wearable/view/Gusterpolator.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.wear.ble.view; - -import android.animation.TimeInterpolator; -import android.annotation.TargetApi; -import android.os.Build; - -/** - * Interpolator that uses a Bezier derived S shaped curve. - * @hide - */ -@TargetApi(Build.VERSION_CODES.KITKAT_WATCH) -class Gusterpolator implements TimeInterpolator { - - /** An instance of {@link androidx.wear.ble.view.Gusterpolator}. */ - public static final Gusterpolator INSTANCE = new Gusterpolator(); - - /** - * To avoid users of this class creating multiple copies needlessly, the constructor is - * private. - */ - private Gusterpolator() {} - - /** - * Lookup table values. - * Generated using a Bezier curve from (0,0) to (1,1) with control points: - * P0 (0,0) - * P1 (0.4, 0) - * P2 (0.2, 1.0) - * P3 (1.0, 1.0) - * - * Values sampled with x at regular intervals between 0 and 1. - */ - private static final float[] VALUES = new float[] { - 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f, - 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f, - 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f, - 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f, - 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f, - 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f, - 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f, - 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f, - 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f, - 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f - }; - - private static final float STEP_SIZE = 1.0f / (VALUES.length - 1); - - @Override - public float getInterpolation(float input) { - if (input >= 1.0f) { - return 1.0f; - } - - if (input <= 0f) { - return 0f; - } - - int position = Math.min( - (int)(input * (VALUES.length - 1)), - VALUES.length - 2); - - float quantized = position * STEP_SIZE; - float difference = input - quantized; - float weight = difference / STEP_SIZE; - - return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]); - } -} diff --git a/PermissionController/src/android/support/wearable/view/ProgressDrawable.java b/PermissionController/src/android/support/wearable/view/ProgressDrawable.java deleted file mode 100644 index 51ec8f07f..000000000 --- a/PermissionController/src/android/support/wearable/view/ProgressDrawable.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.wear.ble.view; - -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.annotation.TargetApi; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.Property; -import android.view.animation.LinearInterpolator; - -/** - * Drawable for showing an indeterminate progress indicator. - * - * TODO: When Material progress drawable is available in the support library stop using this. - * - * @hide - */ -@TargetApi(Build.VERSION_CODES.KITKAT_WATCH) -class ProgressDrawable extends Drawable { - - private static Property<ProgressDrawable, Integer> LEVEL = - new Property<ProgressDrawable, Integer>(Integer.class, "level") { - @Override - public Integer get(ProgressDrawable drawable) { - return drawable.getLevel(); - } - - @Override - public void set(ProgressDrawable drawable, Integer value) { - drawable.setLevel(value); - drawable.invalidateSelf(); - } - }; - /** Max level for a level drawable, as specified in developer docs for {@link Drawable}. */ - private static final int MAX_LEVEL = 10000; - - /** How many different sections are there, five gives us the material style star. **/ - private static final int NUMBER_OF_SEGMENTS = 5; - - private static final int LEVELS_PER_SEGMENT = MAX_LEVEL / NUMBER_OF_SEGMENTS; - private static final float STARTING_ANGLE = -90f; - private static final long ANIMATION_DURATION = 6000; - private static final int FULL_CIRCLE = 360; - private static final int MAX_SWEEP = 306; - private static final int CORRECTION_ANGLE = FULL_CIRCLE - MAX_SWEEP; - /** How far through each cycle does the bar stop growing and start shrinking, half way. **/ - private static final float GROW_SHRINK_RATIO = 0.5f; - // TODO: replace this with BakedBezierInterpolator when its available in support library. - private static final TimeInterpolator mInterpolator = Gusterpolator.INSTANCE; - - private final RectF mInnerCircleBounds = new RectF(); - private final Paint mPaint = new Paint(); - private final ObjectAnimator mAnimator; - private float mCircleBorderWidth; - private int mCircleBorderColor; - - public ProgressDrawable() { - mPaint.setAntiAlias(true); - mPaint.setStyle(Paint.Style.STROKE); - mAnimator = ObjectAnimator.ofInt(this, LEVEL, 0, MAX_LEVEL); - mAnimator.setRepeatCount(ValueAnimator.INFINITE); - mAnimator.setRepeatMode(ValueAnimator.RESTART); - mAnimator.setDuration(ANIMATION_DURATION); - mAnimator.setInterpolator(new LinearInterpolator()); - } - - public void setRingColor(int color) { - mCircleBorderColor = color; - } - - public void setRingWidth(float width) { - mCircleBorderWidth = width; - } - - public void startAnimation() { - mAnimator.start(); - } - - public void stopAnimation() { - mAnimator.cancel(); - } - - @Override - public void draw(Canvas canvas) { - canvas.save(); - mInnerCircleBounds.set(getBounds()); - mInnerCircleBounds.inset(mCircleBorderWidth / 2.0f, mCircleBorderWidth / 2.0f); - mPaint.setStrokeWidth(mCircleBorderWidth); - mPaint.setColor(mCircleBorderColor); - - float sweepAngle = FULL_CIRCLE; - boolean growing = false; - float correctionAngle = 0; - int level = getLevel(); - - int currentSegment = level / LEVELS_PER_SEGMENT; - int offset = currentSegment * LEVELS_PER_SEGMENT; - float progress = (level - offset) / (float) LEVELS_PER_SEGMENT; - - growing = progress < GROW_SHRINK_RATIO; - correctionAngle = CORRECTION_ANGLE * progress; - - if (growing) { - sweepAngle = MAX_SWEEP * mInterpolator.getInterpolation( - lerpInv(0f, GROW_SHRINK_RATIO, progress)); - } else { - sweepAngle = MAX_SWEEP * (1.0f - mInterpolator.getInterpolation( - lerpInv(GROW_SHRINK_RATIO, 1.0f, progress))); - } - - sweepAngle = Math.max(1, sweepAngle); - - canvas.rotate( - level * (1.0f / MAX_LEVEL) * 2 * FULL_CIRCLE + STARTING_ANGLE + correctionAngle, - mInnerCircleBounds.centerX(), - mInnerCircleBounds.centerY()); - canvas.drawArc(mInnerCircleBounds, - growing ? 0 : MAX_SWEEP - sweepAngle, - sweepAngle, - false, - mPaint); - canvas.restore(); - } - - @Override - public void setAlpha(int i) { - // Not supported. - } - - @Override - public void setColorFilter(ColorFilter colorFilter) { - // Not supported. - } - - @Override - public int getOpacity() { - return PixelFormat.OPAQUE; - } - - @Override - protected boolean onLevelChange(int level) { - return true; // Changing the level of this drawable does change its appearance. - } - - /** - * Returns the interpolation scalar (s) that satisfies the equation: - * {@code value = }lerp(a, b, s) - * - * <p>If {@code a == b}, then this function will return 0. - */ - private static float lerpInv(float a, float b, float value) { - return a != b ? ((value - a) / (b - a)) : 0.0f; - } -} diff --git a/PermissionController/src/android/support/wearable/view/SimpleAnimatorListener.java b/PermissionController/src/android/support/wearable/view/SimpleAnimatorListener.java deleted file mode 100644 index c8fe58c43..000000000 --- a/PermissionController/src/android/support/wearable/view/SimpleAnimatorListener.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.wear.ble.view; - -import android.animation.Animator; -import android.annotation.TargetApi; -import android.os.Build; - -/** - * Convenience class for listening for Animator events that implements the AnimatorListener - * interface and allows extending only methods that are necessary. - */ -@TargetApi(Build.VERSION_CODES.KITKAT_WATCH) -public class SimpleAnimatorListener implements Animator.AnimatorListener { - - private boolean mWasCanceled; - - @Override - public void onAnimationCancel(Animator animator) { - mWasCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - if (!mWasCanceled) { - onAnimationComplete(animator); - } - } - - @Override - public void onAnimationRepeat(Animator animator) { - } - - @Override - public void onAnimationStart(Animator animator) { - mWasCanceled = false; - } - - /** - * Called when the animation finishes. Not called if the animation was canceled. - */ - public void onAnimationComplete(Animator animator) { - } - - /** - * Provides information if the animation was cancelled. - * @return True if animation was cancelled. - */ - public boolean wasCanceled() { - return mWasCanceled; - } - -} diff --git a/PermissionController/src/android/support/wearable/view/WearableListView.java b/PermissionController/src/android/support/wearable/view/WearableListView.java deleted file mode 100644 index 487ebfb13..000000000 --- a/PermissionController/src/android/support/wearable/view/WearableListView.java +++ /dev/null @@ -1,1388 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.wear.ble.view; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.PointF; -import android.os.Build; -import android.os.Handler; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.Property; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.Scroller; - -import androidx.recyclerview.widget.LinearSmoothScroller; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.ArrayList; -import java.util.List; - -/** - * An alternative version of ListView that is optimized for ease of use on small screen wearable - * devices. It displays a vertically scrollable list of items, and automatically snaps to the - * nearest item when the user stops scrolling. - * - * <p> - * For a quick start, you will need to implement a subclass of {@link .Adapter}, - * which will create and bind your views to the {@link .ViewHolder} objects. If you want to add - * more visual treatment to your views when they become the central items of the - * WearableListView, have them implement the {@link .OnCenterProximityListener} interface. - * </p> - */ -@TargetApi(Build.VERSION_CODES.KITKAT_WATCH) -public class WearableListView extends RecyclerView { - @SuppressWarnings("unused") - private static final String TAG = "WearableListView"; - - private static final long FLIP_ANIMATION_DURATION_MS = 150; - private static final long CENTERING_ANIMATION_DURATION_MS = 150; - - private static final float TOP_TAP_REGION_PERCENTAGE = .33f; - private static final float BOTTOM_TAP_REGION_PERCENTAGE = .33f; - - // Each item will occupy one third of the height. - private static final int THIRD = 3; - - private final int mMinFlingVelocity; - private final int mMaxFlingVelocity; - - private boolean mMaximizeSingleItem; - private boolean mCanClick = true; - // WristGesture navigation signals are delivered as KeyEvents. Allow developer to disable them - // for this specific View. It might be cleaner to simply have users re-implement onKeyDown(). - // TOOD: Finalize the disabling mechanism here. - private boolean mGestureNavigationEnabled = true; - private int mTapPositionX; - private int mTapPositionY; - private ClickListener mClickListener; - - private Animator mScrollAnimator; - // This is a little hacky due to the fact that animator provides incremental values instead of - // deltas and scrolling code requires deltas. We animate WearableListView directly and use this - // field to calculate deltas. Obviously this means that only one scrolling algorithm can run at - // a time, but I don't think it would be wise to have more than one running. - private int mLastScrollChange; - - private SetScrollVerticallyProperty mSetScrollVerticallyProperty = - new SetScrollVerticallyProperty(); - - private final List<OnScrollListener> mOnScrollListeners = new ArrayList<OnScrollListener>(); - - private final List<OnCentralPositionChangedListener> mOnCentralPositionChangedListeners = - new ArrayList<OnCentralPositionChangedListener>(); - - private OnOverScrollListener mOverScrollListener; - - private boolean mGreedyTouchMode; - - private float mStartX; - - private float mStartY; - - private float mStartFirstTop; - - private final int mTouchSlop; - - private boolean mPossibleVerticalSwipe; - - private int mInitialOffset = 0; - - private Scroller mScroller; - - // Top and bottom boundaries for tap checking. Need to recompute by calling computeTapRegions - // before referencing. - private final float[] mTapRegions = new float[2]; - - private boolean mGestureDirectionLocked; - private int mPreviousCentral = 0; - - // Temp variable for storing locations on screen. - private final int[] mLocation = new int[2]; - - // TODO: Consider clearing this when underlying data set changes. If the data set changes, you - // can't safely assume that this pressed view is in the same place as it was before and it will - // receive setPressed(false) unnecessarily. In theory it should be fine, but in practice we - // have places like this: mIconView.setCircleColor(pressed ? mPressedColor : mSelectedColor); - // This might set selected color on non selected item. Our logic should be: if you change - // underlying data set, all best are off and you need to preserve the state; we will clear - // this field. However, I am not willing to introduce this so late in C development. - private View mPressedView = null; - - private final Runnable mPressedRunnable = new Runnable() { - @Override - public void run() { - if (getChildCount() > 0) { - mPressedView = getChildAt(findCenterViewIndex()); - mPressedView.setPressed(true); - } else { - Log.w(TAG, "mPressedRunnable: the children were removed, skipping."); - } - } - }; - - private final Runnable mReleasedRunnable = new Runnable() { - @Override - public void run() { - releasePressedItem(); - } - }; - - private Runnable mNotifyChildrenPostLayoutRunnable = new Runnable() { - @Override - public void run() { - notifyChildrenAboutProximity(false); - } - }; - - private final AdapterDataObserver mObserver = new AdapterDataObserver() { - @Override - public void onChanged() { - WearableListView.this.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - WearableListView.this.removeOnLayoutChangeListener(this); - if (WearableListView.this.getChildCount() > 0) { - WearableListView.this.animateToCenter(); - } - } - }); - } - }; - - public WearableListView(Context context) { - this(context, null); - } - - public WearableListView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WearableListView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - setHasFixedSize(true); - setOverScrollMode(View.OVER_SCROLL_NEVER); - setLayoutManager(new LayoutManager()); - - final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - if (newState == RecyclerView.SCROLL_STATE_IDLE && getChildCount() > 0) { - handleTouchUp(null, newState); - } - for (OnScrollListener listener : mOnScrollListeners) { - listener.onScrollStateChanged(newState); - } - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - onScroll(dy); - } - }; - setOnScrollListener(onScrollListener); - - final ViewConfiguration vc = ViewConfiguration.get(context); - mTouchSlop = vc.getScaledTouchSlop(); - - mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); - mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); - } - - @Override - public void setAdapter(RecyclerView.Adapter adapter) { - RecyclerView.Adapter currentAdapter = getAdapter(); - if (currentAdapter != null) { - currentAdapter.unregisterAdapterDataObserver(mObserver); - } - - super.setAdapter(adapter); - - if (adapter != null) { - adapter.registerAdapterDataObserver(mObserver); - } - } - - /** - * @return the position of the center child's baseline; -1 if no center child exists or if - * the center child does not return a valid baseline. - */ - @Override - public int getBaseline() { - // No children implies there is no center child for which a baseline can be computed. - if (getChildCount() == 0) { - return super.getBaseline(); - } - - // Compute the baseline of the center child. - final int centerChildIndex = findCenterViewIndex(); - final int centerChildBaseline = getChildAt(centerChildIndex).getBaseline(); - - // If the center child has no baseline, neither does this list view. - if (centerChildBaseline == -1) { - return super.getBaseline(); - } - - return getCentralViewTop() + centerChildBaseline; - } - - /** - * @return true if the list is scrolled all the way to the top. - */ - public boolean isAtTop() { - if (getChildCount() == 0) { - return true; - } - - int centerChildIndex = findCenterViewIndex(); - View centerView = getChildAt(centerChildIndex); - return getChildAdapterPosition(centerView) == 0 && - getScrollState() == RecyclerView.SCROLL_STATE_IDLE; - } - - /** - * Clears the state of the layout manager that positions list items. - */ - public void resetLayoutManager() { - setLayoutManager(new LayoutManager()); - } - - /** - * Controls whether WearableListView should intercept all touch events and also prevent the - * parent from receiving them. - * @param greedy If true it will intercept all touch events. - */ - public void setGreedyTouchMode(boolean greedy) { - mGreedyTouchMode = greedy; - } - - /** - * By default the first element of the list is initially positioned in the center of the screen. - * This method allows the developer to specify a different offset, e.g. to hide the - * WearableListView before the user is allowed to use it. - * - * @param top How far the elements should be pushed down. - */ - public void setInitialOffset(int top) { - mInitialOffset = top; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (!isEnabled()) { - return false; - } - - if (mGreedyTouchMode && getChildCount() > 0) { - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN) { - mStartX = event.getX(); - mStartY = event.getY(); - mStartFirstTop = getChildCount() > 0 ? getChildAt(0).getTop() : 0; - mPossibleVerticalSwipe = true; - mGestureDirectionLocked = false; - } else if (action == MotionEvent.ACTION_MOVE && mPossibleVerticalSwipe) { - handlePossibleVerticalSwipe(event); - } - getParent().requestDisallowInterceptTouchEvent(mPossibleVerticalSwipe); - } - return super.onInterceptTouchEvent(event); - } - - private boolean handlePossibleVerticalSwipe(MotionEvent event) { - if (mGestureDirectionLocked) { - return mPossibleVerticalSwipe; - } - float deltaX = Math.abs(mStartX - event.getX()); - float deltaY = Math.abs(mStartY - event.getY()); - float distance = (deltaX * deltaX) + (deltaY * deltaY); - // Verify that the distance moved in the combined x/y direction is at - // least touch slop before determining the gesture direction. - if (distance > (mTouchSlop * mTouchSlop)) { - if (deltaX > deltaY) { - mPossibleVerticalSwipe = false; - } - mGestureDirectionLocked = true; - } - return mPossibleVerticalSwipe; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!isEnabled()) { - return false; - } - - // super.onTouchEvent can change the state of the scroll, keep a copy so that handleTouchUp - // can exit early if scrollState != IDLE when the touch event started. - int scrollState = getScrollState(); - boolean result = super.onTouchEvent(event); - if (getChildCount() > 0) { - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN) { - handleTouchDown(event); - } else if (action == MotionEvent.ACTION_UP) { - handleTouchUp(event, scrollState); - getParent().requestDisallowInterceptTouchEvent(false); - } else if (action == MotionEvent.ACTION_MOVE) { - if (Math.abs(mTapPositionX - (int) event.getX()) >= mTouchSlop || - Math.abs(mTapPositionY - (int) event.getY()) >= mTouchSlop) { - releasePressedItem(); - mCanClick = false; - } - result |= handlePossibleVerticalSwipe(event); - getParent().requestDisallowInterceptTouchEvent(mPossibleVerticalSwipe); - } else if (action == MotionEvent.ACTION_CANCEL) { - getParent().requestDisallowInterceptTouchEvent(false); - mCanClick = true; - } - } - return result; - } - - private void releasePressedItem() { - if (mPressedView != null) { - mPressedView.setPressed(false); - mPressedView = null; - } - Handler handler = getHandler(); - if (handler != null) { - handler.removeCallbacks(mPressedRunnable); - } - } - - private void onScroll(int dy) { - for (OnScrollListener listener : mOnScrollListeners) { - listener.onScroll(dy); - } - notifyChildrenAboutProximity(true); - } - - /** - * Adds a listener that will be called when the content of the list view is scrolled. - */ - public void addOnScrollListener(OnScrollListener listener) { - mOnScrollListeners.add(listener); - } - - /** - * Removes listener for scroll events. - */ - public void removeOnScrollListener(OnScrollListener listener) { - mOnScrollListeners.remove(listener); - } - - /** - * Adds a listener that will be called when the central item of the list changes. - */ - public void addOnCentralPositionChangedListener(OnCentralPositionChangedListener listener) { - mOnCentralPositionChangedListeners.add(listener); - } - - /** - * Removes a listener that would be called when the central item of the list changes. - */ - public void removeOnCentralPositionChangedListener(OnCentralPositionChangedListener listener) { - mOnCentralPositionChangedListeners.remove(listener); - } - - /** - * Determines if navigation of list with wrist gestures is enabled. - */ - public boolean isGestureNavigationEnabled() { - return mGestureNavigationEnabled; - } - - /** - * Sets whether navigation of list with wrist gestures is enabled. - */ - public void setEnableGestureNavigation(boolean enabled) { - mGestureNavigationEnabled = enabled; - } - - @Override /* KeyEvent.Callback */ - public boolean onKeyDown(int keyCode, KeyEvent event) { - // Respond to keycodes (at least originally generated and injected by wrist gestures). - if (mGestureNavigationEnabled) { - switch (keyCode) { - case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS: - fling(0, -mMinFlingVelocity); - return true; - case KeyEvent.KEYCODE_NAVIGATE_NEXT: - fling(0, mMinFlingVelocity); - return true; - case KeyEvent.KEYCODE_NAVIGATE_IN: - return tapCenterView(); - case KeyEvent.KEYCODE_NAVIGATE_OUT: - // Returing false leaves the action to the container of this WearableListView - // (e.g. finishing the activity containing this WearableListView). - return false; - } - } - return super.onKeyDown(keyCode, event); - } - - /** - * Simulate tapping the child view at the center of this list. - */ - private boolean tapCenterView() { - if (!isEnabled() || getVisibility() != View.VISIBLE) { - return false; - } - int index = findCenterViewIndex(); - View view = getChildAt(index); - ViewHolder holder = getChildViewHolder(view); - if (mClickListener != null) { - mClickListener.onClick(holder); - return true; - } - return false; - } - - private boolean checkForTap(MotionEvent event) { - // No taps are accepted if this view is disabled. - if (!isEnabled()) { - return false; - } - - float rawY = event.getRawY(); - int index = findCenterViewIndex(); - View view = getChildAt(index); - ViewHolder holder = getChildViewHolder(view); - computeTapRegions(mTapRegions); - if (rawY > mTapRegions[0] && rawY < mTapRegions[1]) { - if (mClickListener != null) { - mClickListener.onClick(holder); - } - return true; - } - if (index > 0 && rawY <= mTapRegions[0]) { - animateToMiddle(index - 1, index); - return true; - } - if (index < getChildCount() - 1 && rawY >= mTapRegions[1]) { - animateToMiddle(index + 1, index); - return true; - } - if (index == 0 && rawY <= mTapRegions[0] && mClickListener != null) { - // Special case: if the top third of the screen is empty and the touch event happens - // there, we don't want to immediately disallow the parent from using it. We tell - // parent to disallow intercept only after we locked a gesture. Before that he - // might do something with the action. - mClickListener.onTopEmptyRegionClick(); - return true; - } - return false; - } - - private void animateToMiddle(int newCenterIndex, int oldCenterIndex) { - if (newCenterIndex == oldCenterIndex) { - throw new IllegalArgumentException( - "newCenterIndex must be different from oldCenterIndex"); - } - List<Animator> animators = new ArrayList<Animator>(); - View child = getChildAt(newCenterIndex); - int scrollToMiddle = getCentralViewTop() - child.getTop(); - startScrollAnimation(animators, scrollToMiddle, FLIP_ANIMATION_DURATION_MS); - } - - private void startScrollAnimation(List<Animator> animators, int scroll, long duration) { - startScrollAnimation(animators, scroll, duration, 0); - } - - private void startScrollAnimation(List<Animator> animators, int scroll, long duration, - long delay) { - startScrollAnimation(animators, scroll, duration, delay, null); - } - - private void startScrollAnimation( - int scroll, long duration, long delay, Animator.AnimatorListener listener) { - startScrollAnimation(null, scroll, duration, delay, listener); - } - - private void startScrollAnimation(List<Animator> animators, int scroll, long duration, - long delay, Animator.AnimatorListener listener) { - if (mScrollAnimator != null) { - mScrollAnimator.cancel(); - } - - mLastScrollChange = 0; - ObjectAnimator scrollAnimator = ObjectAnimator.ofInt(this, mSetScrollVerticallyProperty, - 0, -scroll); - - if (animators != null) { - animators.add(scrollAnimator); - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether(animators); - mScrollAnimator = animatorSet; - } else { - mScrollAnimator = scrollAnimator; - } - mScrollAnimator.setDuration(duration); - if (listener != null) { - mScrollAnimator.addListener(listener); - } - if (delay > 0) { - mScrollAnimator.setStartDelay(delay); - } - mScrollAnimator.start(); - } - - @Override - public boolean fling(int velocityX, int velocityY) { - if (getChildCount() == 0) { - return false; - } - // If we are flinging towards empty space (before first element or after last), we reuse - // original flinging mechanism. - final int index = findCenterViewIndex(); - final View child = getChildAt(index); - int currentPosition = getChildPosition(child); - if ((currentPosition == 0 && velocityY < 0) || - (currentPosition == getAdapter().getItemCount() - 1 && velocityY > 0)) { - return super.fling(velocityX, velocityY); - } - - if (Math.abs(velocityY) < mMinFlingVelocity) { - return false; - } - velocityY = Math.max(Math.min(velocityY, mMaxFlingVelocity), -mMaxFlingVelocity); - - if (mScroller == null) { - mScroller = new Scroller(getContext(), null, true); - } - mScroller.fling(0, 0, 0, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, - Integer.MIN_VALUE, Integer.MAX_VALUE); - int finalY = mScroller.getFinalY(); - int delta = finalY / (getPaddingTop() + getAdjustedHeight() / 2); - if (delta == 0) { - // If the fling would not be enough to change position, we increase it to satisfy user's - // intent of switching current position. - delta = velocityY > 0 ? 1 : -1; - } - int finalPosition = Math.max( - 0, Math.min(getAdapter().getItemCount() - 1, currentPosition + delta)); - smoothScrollToPosition(finalPosition); - return true; - } - - public void smoothScrollToPosition(int position, RecyclerView.SmoothScroller smoothScroller) { - LayoutManager layoutManager = (LayoutManager) getLayoutManager(); - layoutManager.setCustomSmoothScroller(smoothScroller); - smoothScrollToPosition(position); - layoutManager.clearCustomSmoothScroller(); - } - - @Override - public ViewHolder getChildViewHolder(View child) { - return (ViewHolder) super.getChildViewHolder(child); - } - - /** - * Adds a listener that will be called when the user taps on the WearableListView or its items. - */ - public void setClickListener(ClickListener clickListener) { - mClickListener = clickListener; - } - - /** - * Adds a listener that will be called when the user drags the top element below its allowed - * bottom position. - * - * @hide - */ - public void setOverScrollListener(OnOverScrollListener listener) { - mOverScrollListener = listener; - } - - private int findCenterViewIndex() { - // TODO(gruszczy): This could be easily optimized, so that we stop looking when we the - // distance starts growing again, instead of finding the closest. It would safe half of - // the loop. - int count = getChildCount(); - int index = -1; - int closest = Integer.MAX_VALUE; - int centerY = getCenterYPos(this); - for (int i = 0; i < count; ++i) { - final View child = getChildAt(i); - int childCenterY = getTop() + getCenterYPos(child); - final int distance = Math.abs(centerY - childCenterY); - if (distance < closest) { - closest = distance; - index = i; - } - } - if (index == -1) { - throw new IllegalStateException("Can't find central view."); - } - return index; - } - - private static int getCenterYPos(View v) { - return v.getTop() + v.getPaddingTop() + getAdjustedHeight(v) / 2; - } - - private void handleTouchUp(MotionEvent event, int scrollState) { - if (mCanClick && event != null && checkForTap(event)) { - Handler handler = getHandler(); - if (handler != null) { - handler.postDelayed(mReleasedRunnable, ViewConfiguration.getTapTimeout()); - } - return; - } - - if (scrollState != RecyclerView.SCROLL_STATE_IDLE) { - // We are flinging, so let's not start animations just yet. Instead we will start them - // when the fling finishes. - return; - } - - if (isOverScrolling()) { - mOverScrollListener.onOverScroll(); - } else { - animateToCenter(); - } - } - - private boolean isOverScrolling() { - return getChildCount() > 0 - // If first view top was below the central top, it means it was never centered. - // Don't allow overscroll, otherwise a simple touch (instead of a drag) will be - // enough to trigger overscroll. - && mStartFirstTop <= getCentralViewTop() - && getChildAt(0).getTop() >= getTopViewMaxTop() - && mOverScrollListener != null; - } - - private int getTopViewMaxTop() { - return getHeight() / 2; - } - - private int getItemHeight() { - // Round up so that the screen is fully occupied by 3 items. - return getAdjustedHeight() / THIRD + 1; - } - - /** - * Returns top of the central {@code View} in the list when such view is fully centered. - * - * This is a more or a less a static value that you can use to align other views with the - * central one. - */ - public int getCentralViewTop() { - return getPaddingTop() + getItemHeight(); - } - - /** - * Automatically starts an animation that snaps the list to center on the element closest to the - * middle. - */ - public void animateToCenter() { - final int index = findCenterViewIndex(); - final View child = getChildAt(index); - final int scrollToMiddle = getCentralViewTop() - child.getTop(); - startScrollAnimation(scrollToMiddle, CENTERING_ANIMATION_DURATION_MS, 0, - new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animator) { - if (!wasCanceled()) { - mCanClick = true; - } - } - }); - } - - /** - * Animate the list so that the first view is back to its initial position. - * @param endAction Action to execute when the animation is done. - * @hide - */ - public void animateToInitialPosition(final Runnable endAction) { - final View child = getChildAt(0); - final int scrollToMiddle = getCentralViewTop() + mInitialOffset - child.getTop(); - startScrollAnimation(scrollToMiddle, CENTERING_ANIMATION_DURATION_MS, 0, - new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animator) { - if (endAction != null) { - endAction.run(); - } - } - }); - } - - private void handleTouchDown(MotionEvent event) { - if (mCanClick) { - mTapPositionX = (int) event.getX(); - mTapPositionY = (int) event.getY(); - float rawY = event.getRawY(); - computeTapRegions(mTapRegions); - if (rawY > mTapRegions[0] && rawY < mTapRegions[1]) { - View view = getChildAt(findCenterViewIndex()); - if (view instanceof OnCenterProximityListener) { - Handler handler = getHandler(); - if (handler != null) { - handler.removeCallbacks(mReleasedRunnable); - handler.postDelayed(mPressedRunnable, ViewConfiguration.getTapTimeout()); - } - } - } - } - } - - private void setScrollVertically(int scroll) { - scrollBy(0, scroll - mLastScrollChange); - mLastScrollChange = scroll; - } - - private int getAdjustedHeight() { - return getAdjustedHeight(this); - } - - private static int getAdjustedHeight(View v) { - return v.getHeight() - v.getPaddingBottom() - v.getPaddingTop(); - } - - private void computeTapRegions(float[] tapRegions) { - mLocation[0] = mLocation[1] = 0; - getLocationOnScreen(mLocation); - int mScreenTop = mLocation[1]; - int height = getHeight(); - tapRegions[0] = mScreenTop + height * TOP_TAP_REGION_PERCENTAGE; - tapRegions[1] = mScreenTop + height * (1 - BOTTOM_TAP_REGION_PERCENTAGE); - } - - /** - * Determines if, when there is only one item in the WearableListView, that the single item - * is laid out so that it's height fills the entire WearableListView. - */ - public boolean getMaximizeSingleItem() { - return mMaximizeSingleItem; - } - - /** - * When set to true, if there is only one item in the WearableListView, it will fill the entire - * WearableListView. When set to false, the default behavior will be used and the single item - * will fill only a third of the screen. - */ - public void setMaximizeSingleItem(boolean maximizeSingleItem) { - mMaximizeSingleItem = maximizeSingleItem; - } - - private void notifyChildrenAboutProximity(boolean animate) { - LayoutManager layoutManager = (LayoutManager) getLayoutManager(); - int count = layoutManager.getChildCount(); - - if (count == 0) { - return; - } - - int index = layoutManager.findCenterViewIndex(); - for (int i = 0; i < count; ++i) { - final View view = layoutManager.getChildAt(i); - ViewHolder holder = getChildViewHolder(view); - holder.onCenterProximity(i == index, animate); - } - final int position = getChildViewHolder(getChildAt(index)).getPosition(); - if (position != mPreviousCentral) { - for (OnScrollListener listener : mOnScrollListeners) { - listener.onCentralPositionChanged(position); - } - for (OnCentralPositionChangedListener listener : - mOnCentralPositionChangedListeners) { - listener.onCentralPositionChanged(position); - } - mPreviousCentral = position; - } - } - - // TODO: Move this to a separate class, so it can't directly interact with the WearableListView. - private class LayoutManager extends RecyclerView.LayoutManager { - private int mFirstPosition; - - private boolean mPushFirstHigher; - - private int mAbsoluteScroll; - - private boolean mUseOldViewTop = true; - - private boolean mWasZoomedIn = false; - - private RecyclerView.SmoothScroller mSmoothScroller; - - private RecyclerView.SmoothScroller mDefaultSmoothScroller; - - // We need to have another copy of the same method, because this one uses - // LayoutManager.getChildCount/getChildAt instead of View.getChildCount/getChildAt and - // they return different values. - private int findCenterViewIndex() { - // TODO(gruszczy): This could be easily optimized, so that we stop looking when we the - // distance starts growing again, instead of finding the closest. It would safe half of - // the loop. - int count = getChildCount(); - int index = -1; - int closest = Integer.MAX_VALUE; - int centerY = getCenterYPos(WearableListView.this); - for (int i = 0; i < count; ++i) { - final View child = getLayoutManager().getChildAt(i); - int childCenterY = getTop() + getCenterYPos(child); - final int distance = Math.abs(centerY - childCenterY); - if (distance < closest) { - closest = distance; - index = i; - } - } - if (index == -1) { - throw new IllegalStateException("Can't find central view."); - } - return index; - } - - @Override - public void onLayoutChildren(RecyclerView.Recycler recycler, State state) { - final int parentBottom = getHeight() - getPaddingBottom(); - // By default we assume this is the first run and the first element will be centered - // with optional initial offset. - int oldTop = getCentralViewTop() + mInitialOffset; - // Here we handle any other situation where we relayout or we want to achieve a - // specific layout of children. - if (mUseOldViewTop && getChildCount() > 0) { - // We are performing a relayout after we already had some children, because e.g. the - // contents of an adapter has changed. First we want to check, if the central item - // from before the layout is still here, because we want to preserve it. - int index = findCenterViewIndex(); - int position = getPosition(getChildAt(index)); - if (position == NO_POSITION) { - // Central item was removed. Let's find the first surviving item and use it - // as an anchor. - for (int i = 0, N = getChildCount(); index + i < N || index - i >= 0; ++i) { - View child = getChildAt(index + i); - if (child != null) { - position = getPosition(child); - if (position != NO_POSITION) { - index = index + i; - break; - } - } - child = getChildAt(index - i); - if (child != null) { - position = getPosition(child); - if (position != NO_POSITION) { - index = index - i; - break; - } - } - } - } - if (position == NO_POSITION) { - // None of the children survives the relayout, let's just use the top of the - // first one. - oldTop = getChildAt(0).getTop(); - int count = state.getItemCount(); - // Lets first make sure that the first position is not above the last element, - // which can happen if elements were removed. - while (mFirstPosition >= count && mFirstPosition > 0) { - mFirstPosition--; - } - } else { - // Some of the children survived the relayout. We will keep it in its place, - // but go through previous children and maybe add them. - if (!mWasZoomedIn) { - // If we were previously zoomed-in on a single item, ignore this and just - // use the default value set above. Reasoning: if we are still zoomed-in, - // oldTop will be ignored when laying out the single child element. If we - // are no longer zoomed in, then we want to position items using the top - // of the single item as if the single item was not zoomed in, which is - // equal to the default value. - oldTop = getChildAt(index).getTop(); - } - while (oldTop > getPaddingTop() && position > 0) { - position--; - oldTop -= getItemHeight(); - } - if (position == 0 && oldTop > getCentralViewTop()) { - // We need to handle special case where the first, central item was removed - // and now the first element is hanging below, instead of being nicely - // centered. - oldTop = getCentralViewTop(); - } - mFirstPosition = position; - } - } else if (mPushFirstHigher) { - // We are trying to position elements ourselves, so we force position of the first - // one. - oldTop = getCentralViewTop() - getItemHeight(); - } - - performLayoutChildren(recycler, state, parentBottom, oldTop); - - // Since the content might have changed, we need to adjust the absolute scroll in case - // some elements have disappeared or were added. - if (getChildCount() == 0) { - setAbsoluteScroll(0); - } else { - View child = getChildAt(findCenterViewIndex()); - setAbsoluteScroll(child.getTop() - getCentralViewTop() + getPosition(child) * - getItemHeight()); - } - - mUseOldViewTop = true; - mPushFirstHigher = false; - } - - private void performLayoutChildren(Recycler recycler, State state, int parentBottom, - int top) { - detachAndScrapAttachedViews(recycler); - - if (mMaximizeSingleItem && state.getItemCount() == 1) { - performLayoutOneChild(recycler, parentBottom); - mWasZoomedIn = true; - } else { - performLayoutMultipleChildren(recycler, state, parentBottom, top); - mWasZoomedIn = false; - } - - if (getChildCount() > 0) { - post(mNotifyChildrenPostLayoutRunnable); - } - } - - private void performLayoutOneChild(Recycler recycler, int parentBottom) { - final int right = getWidth() - getPaddingRight(); - View v = recycler.getViewForPosition(getFirstPosition()); - addView(v, 0); - measureZoomView(v); - v.layout(getPaddingLeft(), getPaddingTop(), right, parentBottom); - } - - private void performLayoutMultipleChildren(Recycler recycler, State state, int parentBottom, - int top) { - int bottom; - final int left = getPaddingLeft(); - final int right = getWidth() - getPaddingRight(); - final int count = state.getItemCount(); - // If we are laying out children with center element being different than the first, we - // need to start with previous child which appears half visible at the top. - for (int i = 0; getFirstPosition() + i < count; i++, top = bottom) { - if (top >= parentBottom) { - break; - } - View v = recycler.getViewForPosition(getFirstPosition() + i); - addView(v, i); - measureThirdView(v); - bottom = top + getItemHeight(); - v.layout(left, top, right, bottom); - } - } - - private void setAbsoluteScroll(int absoluteScroll) { - mAbsoluteScroll = absoluteScroll; - for (OnScrollListener listener : mOnScrollListeners) { - listener.onAbsoluteScrollChange(mAbsoluteScroll); - } - } - - private void measureView(View v, int height) { - final LayoutParams lp = (LayoutParams) v.getLayoutParams(); - final int widthSpec = getChildMeasureSpec(getWidth(), - getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width, - canScrollHorizontally()); - final int heightSpec = getChildMeasureSpec(getHeight(), - getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, - height, canScrollVertically()); - v.measure(widthSpec, heightSpec); - } - - private void measureThirdView(View v) { - measureView(v, (int) (1 + (float) getHeight() / THIRD)); - } - - private void measureZoomView(View v) { - measureView(v, getHeight()); - } - - @Override - public RecyclerView.LayoutParams generateDefaultLayoutParams() { - return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - } - - @Override - public boolean canScrollVertically() { - // Disable vertical scrolling when zoomed. - return getItemCount() != 1 || !mWasZoomedIn; - } - - @Override - public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, State state) { - // TODO(gruszczy): This code is shit, needs to be rewritten. - if (getChildCount() == 0) { - return 0; - } - int scrolled = 0; - final int left = getPaddingLeft(); - final int right = getWidth() - getPaddingRight(); - if (dy < 0) { - while (scrolled > dy) { - final View topView = getChildAt(0); - if (getFirstPosition() > 0) { - final int hangingTop = Math.max(-topView.getTop(), 0); - final int scrollBy = Math.min(scrolled - dy, hangingTop); - scrolled -= scrollBy; - offsetChildrenVertical(scrollBy); - if (getFirstPosition() > 0 && scrolled > dy) { - mFirstPosition--; - View v = recycler.getViewForPosition(getFirstPosition()); - addView(v, 0); - measureThirdView(v); - final int bottom = topView.getTop(); - final int top = bottom - getItemHeight(); - v.layout(left, top, right, bottom); - } else { - break; - } - } else { - mPushFirstHigher = false; - int maxScroll = mOverScrollListener!= null ? - getHeight() : getTopViewMaxTop(); - final int scrollBy = Math.min(-dy + scrolled, maxScroll - topView.getTop()); - scrolled -= scrollBy; - offsetChildrenVertical(scrollBy); - break; - } - } - } else if (dy > 0) { - final int parentHeight = getHeight(); - while (scrolled < dy) { - final View bottomView = getChildAt(getChildCount() - 1); - if (state.getItemCount() > mFirstPosition + getChildCount()) { - final int hangingBottom = - Math.max(bottomView.getBottom() - parentHeight, 0); - final int scrollBy = -Math.min(dy - scrolled, hangingBottom); - scrolled -= scrollBy; - offsetChildrenVertical(scrollBy); - if (scrolled < dy) { - View v = recycler.getViewForPosition(mFirstPosition + getChildCount()); - final int top = getChildAt(getChildCount() - 1).getBottom(); - addView(v); - measureThirdView(v); - final int bottom = top + getItemHeight(); - v.layout(left, top, right, bottom); - } else { - break; - } - } else { - final int scrollBy = - Math.max(-dy + scrolled, getHeight() / 2 - bottomView.getBottom()); - scrolled -= scrollBy; - offsetChildrenVertical(scrollBy); - break; - } - } - } - recycleViewsOutOfBounds(recycler); - setAbsoluteScroll(mAbsoluteScroll + scrolled); - return scrolled; - } - - @Override - public void scrollToPosition(int position) { - mUseOldViewTop = false; - if (position > 0) { - mFirstPosition = position - 1; - mPushFirstHigher = true; - } else { - mFirstPosition = position; - mPushFirstHigher = false; - } - requestLayout(); - } - - public void setCustomSmoothScroller(RecyclerView.SmoothScroller smoothScroller) { - mSmoothScroller = smoothScroller; - } - - public void clearCustomSmoothScroller() { - mSmoothScroller = null; - } - - public RecyclerView.SmoothScroller getDefaultSmoothScroller(RecyclerView recyclerView) { - if (mDefaultSmoothScroller == null) { - mDefaultSmoothScroller = new SmoothScroller( - recyclerView.getContext(), this); - } - return mDefaultSmoothScroller; - } - @Override - public void smoothScrollToPosition(RecyclerView recyclerView, State state, - int position) { - RecyclerView.SmoothScroller scroller = mSmoothScroller; - if (scroller == null) { - scroller = getDefaultSmoothScroller(recyclerView); - } - scroller.setTargetPosition(position); - startSmoothScroll(scroller); - } - - private void recycleViewsOutOfBounds(RecyclerView.Recycler recycler) { - final int childCount = getChildCount(); - final int parentWidth = getWidth(); - // Here we want to use real height, so we don't remove views that are only visible in - // padded section. - final int parentHeight = getHeight(); - boolean foundFirst = false; - int first = 0; - int last = 0; - for (int i = 0; i < childCount; i++) { - final View v = getChildAt(i); - if (v.hasFocus() || (v.getRight() >= 0 && v.getLeft() <= parentWidth && - v.getBottom() >= 0 && v.getTop() <= parentHeight)) { - if (!foundFirst) { - first = i; - foundFirst = true; - } - last = i; - } - } - for (int i = childCount - 1; i > last; i--) { - removeAndRecycleViewAt(i, recycler); - } - for (int i = first - 1; i >= 0; i--) { - removeAndRecycleViewAt(i, recycler); - } - if (getChildCount() == 0) { - mFirstPosition = 0; - } else if (first > 0) { - mPushFirstHigher = true; - mFirstPosition += first; - } - } - - public int getFirstPosition() { - return mFirstPosition; - } - - @Override - public void onAdapterChanged(RecyclerView.Adapter oldAdapter, - RecyclerView.Adapter newAdapter) { - removeAllViews(); - } - } - - /** - * Interface for receiving callbacks when WearableListView children become or cease to be the - * central item. - */ - public interface OnCenterProximityListener { - /** - * Called when this view becomes central item of the WearableListView. - * - * @param animate Whether you should animate your transition of the View to become the - * central item. If false, this is the initial setting and you should - * transition immediately. - */ - void onCenterPosition(boolean animate); - - /** - * Called when this view stops being the central item of the WearableListView. - * @param animate Whether you should animate your transition of the View to being - * non central item. If false, this is the initial setting and you should - * transition immediately. - */ - void onNonCenterPosition(boolean animate); - } - - /** - * Interface for listening for click events on WearableListView. - */ - public interface ClickListener { - /** - * Called when the central child of the WearableListView is tapped. - * @param view View that was clicked. - */ - public void onClick(ViewHolder view); - - /** - * Called when the user taps the top third of the WearableListView and no item is present - * there. This can happen when you are in initial state and the first, top-most item of the - * WearableListView is centered. - */ - public void onTopEmptyRegionClick(); - } - - /** - * @hide - */ - public interface OnOverScrollListener { - public void onOverScroll(); - } - - /** - * Interface for listening to WearableListView content scrolling. - */ - public interface OnScrollListener { - /** - * Called when the content is scrolled, reporting the relative scroll value. - * @param scroll Amount the content was scrolled. This is a delta from the previous - * position to the new position. - */ - public void onScroll(int scroll); - - /** - * Called when the content is scrolled, reporting the absolute scroll value. - * - * @deprecated BE ADVISED DO NOT USE THIS This might provide wrong values when contents - * of a RecyclerView change. - * - * @param scroll Absolute scroll position of the content inside the WearableListView. - */ - @Deprecated - public void onAbsoluteScrollChange(int scroll); - - /** - * Called when WearableListView's scroll state changes. - * - * @param scrollState The updated scroll state. One of {@link #SCROLL_STATE_IDLE}, - * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}. - */ - public void onScrollStateChanged(int scrollState); - - /** - * Called when the central item of the WearableListView changes. - * - * @param centralPosition Position of the item in the Adapter. - */ - public void onCentralPositionChanged(int centralPosition); - } - - /** - * A listener interface that can be added to the WearableListView to get notified when the - * central item is changed. - */ - public interface OnCentralPositionChangedListener { - /** - * Called when the central item of the WearableListView changes. - * - * @param centralPosition Position of the item in the Adapter. - */ - void onCentralPositionChanged(int centralPosition); - } - - /** - * Base class for adapters providing data for the WearableListView. For details refer to - * RecyclerView.Adapter. - */ - public static abstract class Adapter extends RecyclerView.Adapter<ViewHolder> { - } - - private static class SmoothScroller extends LinearSmoothScroller { - - private static final float MILLISECONDS_PER_INCH = 100f; - - private final LayoutManager mLayoutManager; - - public SmoothScroller(Context context, WearableListView.LayoutManager manager) { - super(context); - mLayoutManager = manager; - } - - @Override - protected void onStart() { - super.onStart(); - } - - // TODO: (mindyp): when flinging, return the dydt that triggered the fling. - @Override - protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { - return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; - } - - @Override - public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int - snapPreference) { - // Snap to center. - return (boxStart + boxEnd) / 2 - (viewStart + viewEnd) / 2; - } - - @Override - public PointF computeScrollVectorForPosition(int targetPosition) { - if (targetPosition < mLayoutManager.getFirstPosition()) { - return new PointF(0, -1); - } else { - return new PointF(0, 1); - } - } - } - - /** - * Wrapper around items displayed in the list view. {@link .Adapter} must return objects that - * are instances of this class. Consider making the wrapped View implement - * {@link .OnCenterProximityListener} if you want to receive a callback when it becomes or - * ceases to be the central item in the WearableListView. - */ - public static class ViewHolder extends RecyclerView.ViewHolder { - public ViewHolder(View itemView) { - super(itemView); - } - - /** - * Called when the wrapped view is becoming or ceasing to be the central item of the - * WearableListView. - * - * Retained as protected for backwards compatibility. - * - * @hide - */ - protected void onCenterProximity(boolean isCentralItem, boolean animate) { - if (!(itemView instanceof OnCenterProximityListener)) { - return; - } - OnCenterProximityListener item = (OnCenterProximityListener) itemView; - if (isCentralItem) { - item.onCenterPosition(animate); - } else { - item.onNonCenterPosition(animate); - } - } - } - - private class SetScrollVerticallyProperty extends Property<WearableListView, Integer> { - public SetScrollVerticallyProperty() { - super(Integer.class, "scrollVertically"); - } - - @Override - public Integer get(WearableListView wearableListView) { - return wearableListView.mLastScrollChange; - } - - @Override - public void set(WearableListView wearableListView, Integer value) { - wearableListView.setScrollVertically(value); - } - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java b/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java index 60caf888c..ec3642d1d 100644 --- a/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java @@ -18,12 +18,10 @@ package com.android.permissioncontroller; import android.content.Context; import android.content.pm.PackageManager; -import android.content.res.Configuration; public class DeviceUtils { public static boolean isTelevision(Context context) { - int uiMode = context.getResources().getConfiguration().uiMode; - return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION; + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); } public static boolean isWear(final Context context) { diff --git a/PermissionController/src/com/android/permissioncontroller/PermissionController.proto b/PermissionController/src/com/android/permissioncontroller/PermissionController.proto index 429909b71..02e51ce2d 100644 --- a/PermissionController/src/com/android/permissioncontroller/PermissionController.proto +++ b/PermissionController/src/com/android/permissioncontroller/PermissionController.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package com.android.permissioncontroller; option java_outer_classname = "PermissionControllerProto"; -import "packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.proto"; +import "permission/service/AutoRevokePermissions.proto"; message PermissionControllerDumpProto { optional permission.service.AutoRevokePermissionsDumpProto autoRevoke = 1; diff --git a/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java index bc7a1d051..dad0dc37a 100644 --- a/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java +++ b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java @@ -27,11 +27,14 @@ import android.view.accessibility.AccessibilityManager; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.permissioncontroller.privacysources.SafetyCenterAccessibilityListener; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.ui.SpecialAppAccessListActivity; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleParserInitializer; +import com.android.role.controller.model.Roles; public final class PermissionControllerApplication extends Application { @@ -44,10 +47,14 @@ public final class PermissionControllerApplication extends Application { sInstance = this; PackageItemInfo.forceSafeLabels(); + RoleParserInitializer.initialize(); updateSpecialAppAccessListActivityEnabledState(); if (SdkLevel.isAtLeastT()) { addAccessibilityListener(); } + if (Utils.isHealthPermissionUiEnabled()) { + KotlinUtils.INSTANCE.addHealthPermissions(this); + } } /** @@ -64,7 +71,7 @@ public final class PermissionControllerApplication extends Application { for (int i = 0; i < rolesSize; i++) { Role role = roles.valueAt(i); - if (!role.isAvailable(this) || !role.isVisible(this)) { + if (!role.isAvailable(this) || !RoleUiBehaviorUtils.isVisible(role, this)) { continue; } if (!role.isExclusive()) { diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/OWNERS b/PermissionController/src/com/android/permissioncontroller/hibernation/OWNERS new file mode 100644 index 000000000..21ab3aa28 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/core/java/android/apphibernation/OWNERS diff --git a/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java b/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java index 95fd2516f..d19ef460a 100644 --- a/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java +++ b/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java @@ -100,7 +100,7 @@ public class ReportDetails { final IncidentHeaderProto header = incident.getHeader(i); if (header.hasReason()) { final String reason = header.getReason(); - if (reason != null && reason.length() > 0) { + if (reason.length() > 0) { result.add(reason); } } @@ -122,9 +122,6 @@ public class ReportDetails { final int setsCount = section.getSetsCount(); for (int i = 0; i < setsCount; i++) { final RestrictedImageSetProto set = section.getSets(i); - if (set == null) { - continue; - } final int imageCount = set.getImagesCount(); for (int j = 0; j < imageCount; j++) { // Hard cap on number of images, as a guardrail. @@ -135,18 +132,12 @@ public class ReportDetails { } final RestrictedImageProto image = set.getImages(j); - if (image == null) { - continue; - } final String mimeType = image.getMimeType(); if (!("image/jpeg".equals(mimeType) || "image/png".equals(mimeType))) { throw new ParseException("Unsupported image type " + mimeType); } final ByteString bytes = image.getImageData(); - if (bytes == null) { - continue; - } final byte[] buf = bytes.toByteArray(); if (buf.length == 0) { continue; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING index e9b30d20a..b65eb6710 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING @@ -5,6 +5,24 @@ "options": [ { "include-filter": "android.permission.cts.BackgroundPermissionsTest" + }, + { + "include-filter": "android.permission.cts.LocationAccessCheckTest" + }, + { + "include-filter": "android.permission.cts.NotificationListenerCheckTest" + }, + { + "include-filter": "android.permission.cts.OneTimePermissionTest" + }, + { + "include-filter": "android.permission.cts.PermissionControllerTest" + }, + { + "include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" } ] }, @@ -12,16 +30,46 @@ "name": "CtsHibernationTestCases", "options": [ { - "include-filter": "android.hibernation.cts.AutoRevokeTest" + "exclude-annotation": "android.platform.test.annotations.FlakyTest" } ] } ], - "presubmit-large": [ + "mainline-presubmit": [ { - "name": "CtsPermission3TestCases", + "name": "CtsPermissionTestCases[com.google.android.permission.apex]", "options": [ { + "include-filter": "android.permission.cts.BackgroundPermissionsTest" + }, + { + "include-filter": "android.permission.cts.LocationAccessCheckTest" + }, + { + "include-filter": "android.permission.cts.NotificationListenerCheckTest" + }, + { + "include-filter": "android.permission.cts.OneTimePermissionTest" + }, + { + "include-filter": "android.permission.cts.PermissionControllerTest" + }, + { + "include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + }, + { + "name": "CtsHibernationTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677038): This test currently fails on R base image + { + "exclude-filter": "android.hibernation.cts.AutoRevokeTest#testUnusedApp_uninstallApp" + }, + { "exclude-annotation": "android.platform.test.annotations.FlakyTest" } ] diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt index 0318144e4..5d91ebfda 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt @@ -18,6 +18,8 @@ package com.android.permissioncontroller.permission.data import android.app.Application import android.app.role.RoleManager +import android.os.Handler +import android.os.Looper import android.os.UserHandle import androidx.lifecycle.LiveData import com.android.permissioncontroller.PermissionControllerApplication @@ -34,11 +36,24 @@ import com.android.permissioncontroller.permission.utils.Utils */ class PermGroupsPackagesUiInfoLiveData( private val app: Application, - groupNamesLiveData: LiveData<List<String>> + private val groupNamesLiveData: LiveData<List<String>> ) : SmartUpdateMediatorLiveData< @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?>>() { private val SYSTEM_SHELL = "android.app.role.SYSTEM_SHELL" + private val STAGGER_LOAD_TIME_MS = 50L + + // Optimization: This LiveData relies on a large number of other ones. Enough that they can + // cause loading issues when they all become active at once. If this value is true, then it will + // slowly load data from all source LiveDatas, spacing loads them STAGGER_LOAD_TIME_MS apart + var loadStaggered: Boolean = false + + // If we are returning from a particular permission group page, then that particular group is + // the one most likely to change. If so, then it will be prioritized in the load order. + var firstLoadGroup: String? = null + + private val handler: Handler = Handler(Looper.getMainLooper()) + /** * Map<permission group name, PermGroupUiLiveDatas> */ @@ -139,4 +154,39 @@ class PermGroupsPackagesUiInfoLiveData( } value = allPackageData.toMap() } + + // Schedule a staggered loading of individual permission group livedatas + private fun scheduleStaggeredGroupLoad() { + if (groupNamesLiveData.value != null) { + if (firstLoadGroup in groupNames) { + addLiveDataDelayed(firstLoadGroup!!, 0) + } + for ((idx, groupName) in groupNames.withIndex()) { + if (groupName != firstLoadGroup) { + addLiveDataDelayed(groupName, idx * STAGGER_LOAD_TIME_MS) + } + } + } + } + + private fun addLiveDataDelayed(groupName: String, delayTimeMs: Long) { + val liveData = SinglePermGroupPackagesUiInfoLiveData[groupName] + permGroupPackagesLiveDatas[groupName] = liveData + handler.postDelayed( { addSource(liveData) { update() } }, delayTimeMs) + } + + override fun onActive() { + super.onActive() + if (loadStaggered && permGroupPackagesLiveDatas.isEmpty()) { + scheduleStaggeredGroupLoad() + } + } + + override fun onInactive() { + super.onInactive() + if (loadStaggered) { + permGroupPackagesLiveDatas.forEach { (_, liveData) -> removeSource(liveData) } + permGroupPackagesLiveDatas.clear() + } + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt new file mode 100644 index 000000000..48909384f --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt @@ -0,0 +1,116 @@ +/* + * 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.permissioncontroller.permission.data + +import android.app.Application +import android.content.pm.PackageManager +import android.os.PersistableBundle +import android.util.Log +import com.android.permission.safetylabel.DataCategoryConstants +import com.android.permission.safetylabel.DataLabelConstants +import com.android.permission.safetylabel.DataTypeConstants +import com.android.permission.safetylabel.SafetyLabel +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissionRationaleEnabled +import com.android.permissioncontroller.permission.utils.KotlinUtils.isPlaceholderSafetyLabelDataEnabled +import kotlinx.coroutines.Job + +/** + * SafetyLabel LiveData for the specified package + * + * @param app current Application + * @param packageName name of the package to get SafetyLabel information for + */ +class SafetyLabelLiveData +private constructor(private val app: Application, private val packageName: String) : + SmartAsyncMediatorLiveData<SafetyLabel>() { + + override suspend fun loadDataAndPostValue(job: Job) { + if (job.isCancelled) { + return + } + + if (!isPermissionRationaleEnabled()) { + postValue(null) + return + } + + if (packageName.isEmpty()) { + postValue(null) + return + } + + val safetyLabel: SafetyLabel? = + try { + val metadataBundle: PersistableBundle? = getInstallMetadataBundle() + SafetyLabel.getSafetyLabelFromMetadata(metadataBundle) + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "SafetyLabel for $packageName not found") + invalidateSingle(packageName) + null + } + postValue(safetyLabel) + } + + // TODO(b/257293222): Update when hooking up PackageManager APIs + private fun getInstallMetadataBundle(): PersistableBundle? { + return if (isPlaceholderSafetyLabelDataEnabled()) { + placeholderMetadataBundle() + } else { + null + } + } + + // TODO(b/257293222): Remove when hooking up PackageManager APIs + private fun placeholderMetadataBundle(): PersistableBundle { + val approximateLocationBundle = PersistableBundle().apply { + putIntArray( + "purposes", + (1..7).toList().toIntArray()) + } + + val locationBundle = PersistableBundle().apply { + putPersistableBundle( + DataTypeConstants.LOCATION_APPROX_LOCATION, + approximateLocationBundle) + } + + val dataSharedBundle = PersistableBundle().apply { + putPersistableBundle(DataCategoryConstants.CATEGORY_LOCATION, locationBundle) + } + + val dataLabelBundle = PersistableBundle().apply { + putPersistableBundle(DataLabelConstants.DATA_USAGE_SHARED, dataSharedBundle) + } + + val safetyLabelBundle = PersistableBundle().apply { + putPersistableBundle("data_labels", dataLabelBundle) + } + + return PersistableBundle().apply { + putPersistableBundle("safety_labels", safetyLabelBundle) + } + } + + companion object : DataRepositoryForPackage<String, SafetyLabelLiveData>() { + private val LOG_TAG = SafetyLabelLiveData::class.java.simpleName + + override fun newValue(key: String): SafetyLabelLiveData { + return SafetyLabelLiveData(PermissionControllerApplication.get(), key) + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt index c74567d52..cca266721 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt @@ -36,8 +36,8 @@ import kotlinx.coroutines.launch * * @param isStaticVal Whether or not this LiveData value is expected to change */ -abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = false) - : MediatorLiveData<T>(), DataRepository.InactiveTimekeeper { +abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = false) : + MediatorLiveData<T>(), DataRepository.InactiveTimekeeper { companion object { const val DEBUG_UPDATES = false @@ -146,8 +146,9 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = } try { super.addSource(source, onChanged) - } catch (other: IllegalStateException) { - throw other.apply { initCause(exception) } + } catch (ex: IllegalStateException) { + val other = ex as java.lang.Throwable + throw other.initCause(exception) } } } @@ -179,7 +180,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = have: MutableMap<K, V>, getLiveDataFun: (K) -> V, onUpdateFun: ((K) -> Unit)? = null - ) : Pair<Set<K>, Set<K>>{ + ): Pair<Set<K>, Set<K>>{ // Ensure the map is correct when method returns val (toAdd, toRemove) = KotlinUtils.getMapAndListDifferences(desired, have) for (key in toAdd) { @@ -252,4 +253,4 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = }, isInitialized = { isInitialized && (staleOk || !isStale) }) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt new file mode 100644 index 000000000..421fc5891 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt @@ -0,0 +1,106 @@ +/* + * 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.permissioncontroller.permission.data.v31 + +import android.app.AppOpsManager +import android.app.Application +import android.os.UserHandle +import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData +import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData +import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps +import com.android.permissioncontroller.permission.utils.PermissionMapping +import kotlinx.coroutines.Job + +/** + * LiveData class tracking [LightPackageOps] for all packages on the device and for all system + * permission groups' ops. + * + * App ops data is retrieved from [AppOpsManager] and is updated whenever app ops data changes are + * heard. + */ +// TODO(b/258257361): Implement OnOpNotedListener once it is exposed as API. +class AllLightPackageOpsLiveData(app: Application) : + SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, LightPackageOps>>(), + AppOpsManager.OnOpActiveChangedListener, + AppOpsManager.OnOpChangedListener { + + private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! + private var opNames: Set<String> = getOpNames(StandardPermGroupNamesLiveData.value) + + init { + addSource(StandardPermGroupNamesLiveData) { + opNames = getOpNames(it) + update() + } + } + + override fun onActive() { + super.onActive() + + try { + appOpsManager.startWatchingActive(opNames.toTypedArray(), { it.run() }, this) + } catch (ignored: IllegalArgumentException) { + // Older builds may not support all requested app ops. + } + + opNames.forEach { + try { + appOpsManager.startWatchingMode(it, /* all packages */ null, this) + } catch (ignored: IllegalArgumentException) { + // Older builds may not support all requested app ops. + } + } + } + + override fun onInactive() { + super.onInactive() + + appOpsManager.stopWatchingActive(this) + appOpsManager.stopWatchingMode(this) + } + + override suspend fun loadDataAndPostValue(job: Job) { + val packageOpsList = + try { + appOpsManager.getPackagesForOps(opNames.toTypedArray()) + } catch (e: NullPointerException) { + // Older builds may not support all requested app ops. + emptyList<AppOpsManager.PackageOps>() + } + + postValue( + packageOpsList.associateBy( + { Pair(it.packageName, UserHandle.getUserHandleForUid(it.uid)) }, + { LightPackageOps(opNames, it) })) + } + + override fun onOpChanged(op: String?, packageName: String?) { + update() + } + + override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) { + update() + } + + /** Returns all op names for all permissions in a list of permission groups. */ + private fun getOpNames(permissionGroupNames: List<String>?) = + permissionGroupNames + ?.flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) } + ?.mapNotNull { permName -> AppOpsManager.permissionToOp(permName) } + ?.toSet() + ?: setOf() +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java index 555ba8d51..eed017d0f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java @@ -24,6 +24,9 @@ import static android.app.AppOpsManager.MODE_FOREGROUND; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP; + +import static com.android.permissioncontroller.permission.utils.Utils.isHealthPermissionUiEnabled; import android.Manifest; import android.app.ActivityManager; @@ -337,6 +340,8 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup> & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName) + || (isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals( + requestedPermissionInfo.group)) ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null; final boolean appOpAllowed; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt index cb3c6acd3..fd7d82dfc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt @@ -16,6 +16,7 @@ package com.android.permissioncontroller.permission.model.livedatatypes +import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PermissionInfo import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission @@ -71,6 +72,20 @@ data class LightPermission( val isOneTime = flags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 /** Whether this permission is an instant app permission */ val isInstantPerm = permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT != 0 + /** Whether this permission is implicitly added to the package */ + val isImplicit: Boolean by lazy { + var implicit = false + for ((permName, permFlags) in + pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) { + if (permName == permInfo.name && + (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0 + ) { + implicit = true + break + } + } + implicit + } /** Whether this permission is a runtime only permission */ val isRuntimeOnly = permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY != 0 @@ -124,4 +139,4 @@ data class LightPermission( if (isAutoRevoked) append(", AutoRevoked") if (isSelectedLocationAccuracy) append(", SelectedLocationAccuracy") } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt new file mode 100644 index 000000000..87426eb35 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt @@ -0,0 +1,94 @@ +/* + * 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.permissioncontroller.permission.model.livedatatypes.v31 + +import android.app.AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA +import android.app.AppOpsManager.OP_FLAG_SELF +import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED +import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY +import android.app.AppOpsManager.PackageOps +import android.app.AppOpsManager.opToPermission +import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP +import android.os.UserHandle +import com.android.permissioncontroller.permission.utils.PermissionMapping.getGroupOfPlatformPermission + +/** + * Light version of [PackageOps] class, tracking the last permission access for system permission + * groups. + */ +data class LightPackageOps( + /** Name of the package. */ + val packageName: String, + /** [UserHandle] running the package. */ + val userHandle: UserHandle, + /** + * Mapping of permission group name to the last access time of any op backing a permission in + * the group. + */ + val lastPermissionGroupAccessTimesMs: Map<String, Long> +) { + constructor( + ops: Set<String>, + packageOps: PackageOps + ) : this( + packageOps.packageName, + UserHandle.getUserHandleForUid(packageOps.uid), + createLastPermissionGroupAccessTimesMap(ops, packageOps)) + + /** Companion object for [LightPackageOps]. */ + companion object { + /** Flags to use for querying an op's last access time. */ + private const val OPS_LAST_ACCESS_FLAGS = + OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED or OP_FLAG_TRUSTED_PROXY + + /** Creates a mapping from permission group to the last time it was accessed. */ + private fun createLastPermissionGroupAccessTimesMap( + opNames: Set<String>, + packageOps: PackageOps + ): Map<String, Long> { + val lastAccessTimeMs = mutableMapOf<String, Long>() + // Add keys for all permissions groups covered by the provided ops, regardless of + // whether they have been observed recently. + for (permissionGroup in opNames.mapNotNull { getPermissionGroupForOp(it) }.toSet()) { + lastAccessTimeMs[permissionGroup] = -1 + } + + for (opEntry in packageOps.ops) { + val permissionGroupOfOp = getPermissionGroupForOp(opEntry.opStr) ?: continue + lastAccessTimeMs[permissionGroupOfOp] = + maxOf( + lastAccessTimeMs[permissionGroupOfOp] ?: -1, + opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS)) + } + + return lastAccessTimeMs + } + + /** Returns the permission group for the permission that the provided op backs, if any. */ + private fun getPermissionGroupForOp(opName: String): String? { + // The OPSTR_READ_WRITE_HEALTH_DATA is a special case as unlike other ops, it does not + // map to a single permission. However it is safe to retrieve a permission group for it, + // as all permissions it maps to, map to the same permission group + // HEALTH_PERMISSION_GROUP. + if (opName == OPSTR_READ_WRITE_HEALTH_DATA) { + return HEALTH_PERMISSION_GROUP + } + + return opToPermission(opName)?.let { getGroupOfPlatformPermission(it) } + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java index 8622ef3ab..b7cddace2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java @@ -125,16 +125,6 @@ public final class AppPermissionUsage { return lastAccessTime; } - public long getAccessCount() { - long accessCount = 0; - final int permissionCount = mGroupUsages.size(); - for (int i = 0; i < permissionCount; i++) { - final GroupUsage permission = mGroupUsages.get(i); - accessCount += permission.getAccessCount(); - } - return accessCount; - } - public @NonNull List<GroupUsage> getGroupUsages() { return mGroupUsages; } @@ -163,62 +153,6 @@ public final class AppPermissionUsage { return lastAccessAggregate((op) -> op.getLastAccessTime(PRIVACY_HUB_FLAGS)); } - public long getLastAccessForegroundTime() { - if (mLastUsage == null) { - return 0; - } - - return lastAccessAggregate((op) -> op.getLastAccessForegroundTime(PRIVACY_HUB_FLAGS)); - } - - public long getLastAccessBackgroundTime() { - if (mLastUsage == null) { - return 0; - } - - return lastAccessAggregate((op) -> op.getLastAccessBackgroundTime(PRIVACY_HUB_FLAGS)); - } - - public long getForegroundAccessCount() { - if (mHistoricalUsage == null) { - return 0; - } - - return extractAggregate((HistoricalOp op) - -> op.getForegroundAccessCount(PRIVACY_HUB_FLAGS)); - } - - public long getBackgroundAccessCount() { - if (mHistoricalUsage == null) { - return 0; - } - - return extractAggregate((HistoricalOp op) - -> op.getBackgroundAccessCount(PRIVACY_HUB_FLAGS)); - } - - public long getAccessCount() { - if (mHistoricalUsage == null) { - return 0; - } - - return extractAggregate((HistoricalOp op) -> - op.getForegroundAccessCount(PRIVACY_HUB_FLAGS) - + op.getBackgroundAccessCount(PRIVACY_HUB_FLAGS) - ); - } - - /** - * Get the last access duration. - */ - public long getLastAccessDuration() { - if (mLastUsage == null) { - return 0; - } - return lastAccessAggregate( - (op) -> op.getLastDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)); - } - /** * Get the access duration. */ @@ -508,11 +442,12 @@ public final class AppPermissionUsage { } AttributionLabelledGroupUsage build() { + ArrayList<String> attributionTagsList = new ArrayList<>(); + attributionTagsList.addAll(mAttributionTags); return new AttributionLabelledGroupUsage(mLabel, mAppPermissionGroup, - new ArrayList<String>() {{ - addAll(mAttributionTags); - }}, mDiscreteAccessTime); + attributionTagsList, + mDiscreteAccessTime); } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java index a8044796a..6e37973ed 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java @@ -20,8 +20,10 @@ import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.RECORD_AUDIO; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE; +import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP; import static com.android.permissioncontroller.Constants.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO; +import static com.android.permissioncontroller.permission.utils.Utils.isHealthPermissionUiEnabled; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; @@ -233,7 +235,9 @@ public final class PermissionUsages implements LoaderCallbacks<List<AppPermissio for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) { final PermissionGroup group = groups.get(groupIdx); // Filter out third party permissions - if (!group.getDeclaringPackage().equals(Utils.OS_PKG)) { + if (!(group.getDeclaringPackage().equals(Utils.OS_PKG) + || (isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals( + group.getName())))) { continue; } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java index e0e124f54..6d58d96d2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java @@ -160,7 +160,7 @@ import java.util.stream.Collectors; */ public class LocationAccessCheck { private static final String LOG_TAG = LocationAccessCheck.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private static final long DEFAULT_RENOTIFY_DURATION_MILLIS = DAYS.toMillis(90); private static final String ISSUE_ID_PREFIX = "bg_location_"; private static final String ISSUE_TYPE_ID = "bg_location_privacy_issue"; @@ -410,6 +410,7 @@ public class LocationAccessCheck { private void addLocationNotificationIfNeeded(@NonNull JobParameters params, @NonNull LocationAccessCheckJobService service) { if (!checkLocationAccessCheckEnabledAndUpdateEnabledTime()) { + Log.v(LOG_TAG, "LocationAccessCheck feature is not enabled."); service.jobFinished(params, false); return; } @@ -419,11 +420,13 @@ public class LocationAccessCheck { if (currentTimeMillis() - mSharedPrefs.getLong( KEY_LAST_LOCATION_ACCESS_NOTIFICATION_SHOWN, 0) < getInBetweenNotificationsMillis()) { + Log.v(LOG_TAG, "location notification interval is not enough."); service.jobFinished(params, false); return; } if (getCurrentlyShownNotificationLocked() != null) { + Log.v(LOG_TAG, "already location notification exist."); service.jobFinished(params, false); return; } @@ -437,6 +440,7 @@ public class LocationAccessCheck { } finally { synchronized (sLock) { service.mAddLocationNotificationIfNeededTask = null; + Log.v(LOG_TAG, "LocationAccessCheck privacy job marked complete."); } } } @@ -447,6 +451,10 @@ public class LocationAccessCheck { synchronized (sLock) { List<UserPackage> packages = getLocationUsersLocked(ops); ArraySet<UserPackage> alreadyNotifiedPackages = loadAlreadyNotifiedPackagesLocked(); + if (DEBUG) { + Log.v(LOG_TAG, "location packages: " + packages); + Log.v(LOG_TAG, "already notified packages: " + alreadyNotifiedPackages); + } throwInterruptedExceptionIfTaskIsCanceled(); // Send these issues to safety center if (isSafetyCenterBgLocationReminderEnabled()) { @@ -462,6 +470,9 @@ public class LocationAccessCheck { throwInterruptedExceptionIfTaskIsCanceled(); if (packages.isEmpty()) { + if (DEBUG) { + Log.v(LOG_TAG, "No package found to send a notification"); + } return; } @@ -1152,6 +1163,7 @@ public class LocationAccessCheck { @Override public void onCreate() { + Log.v(LOG_TAG, "LocationAccessCheck privacy job is created"); super.onCreate(); mLocationAccessCheck = new LocationAccessCheck(this, () -> { synchronized (sLock) { @@ -1170,8 +1182,10 @@ public class LocationAccessCheck { */ @Override public boolean onStartJob(JobParameters params) { + Log.v(LOG_TAG, "LocationAccessCheck privacy job is started"); synchronized (LocationAccessCheck.sLock) { if (mAddLocationNotificationIfNeededTask != null) { + Log.v(LOG_TAG, "LocationAccessCheck old job not completed yet."); return false; } @@ -1192,6 +1206,7 @@ public class LocationAccessCheck { */ @Override public boolean onStopJob(JobParameters params) { + Log.v(LOG_TAG, "LocationAccessCheck privacy source onStopJob called."); AddLocationNotificationIfNeededTask task; synchronized (sLock) { if (mAddLocationNotificationIfNeededTask == null) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java index 2a090f662..5e03e721c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java @@ -64,8 +64,8 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.PermissionMapping; import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt index af151320f..b4a0d73ea 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt @@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.service.v33 import android.content.ComponentName import android.content.Intent import android.content.pm.PackageManager +import android.graphics.drawable.Icon import android.os.IBinder import android.provider.DeviceConfig import android.safetycenter.SafetyCenterManager diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java index 296a007aa..37acc9ef0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java @@ -18,6 +18,8 @@ package com.android.permissioncontroller.permission.ui; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission_group.READ_MEDIA_VISUAL; +import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED; @@ -26,7 +28,10 @@ import static com.android.permissioncontroller.permission.ui.GrantPermissionsVie import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_SETTINGS; +import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.APP_PERMISSION_REQUEST_CODE; +import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.PHOTO_PICKER_REQUEST_CODE; import static com.android.permissioncontroller.permission.utils.Utils.getRequestMessage; import android.Manifest; @@ -36,6 +41,7 @@ import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.drawable.Icon; +import android.icu.lang.UCharacter; import android.os.Bundle; import android.os.Process; import android.text.Annotation; @@ -59,9 +65,9 @@ import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.ui.auto.GrantPermissionsAutoViewHandler; -import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModel; -import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModel.RequestInfo; -import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModelFactory; +import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel; +import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.RequestInfo; +import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModelFactory; import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; @@ -71,6 +77,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Random; /** @@ -85,7 +92,7 @@ public class GrantPermissionsActivity extends SettingsActivity + "_REQUEST_ID"; public static final String ANNOTATION_ID = "link"; - public static final int NEXT_BUTTON = 11; + public static final int NEXT_BUTTON = 15; public static final int ALLOW_BUTTON = 0; public static final int ALLOW_ALWAYS_BUTTON = 1; // Used in auto public static final int ALLOW_FOREGROUND_BUTTON = 2; @@ -97,6 +104,10 @@ public class GrantPermissionsActivity extends SettingsActivity public static final int NO_UPGRADE_OT_BUTTON = 8; // one-time public static final int NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON = 9; // one-time public static final int LINK_TO_SETTINGS = 10; + public static final int ALLOW_ALL_PHOTOS_BUTTON = 11; + public static final int ALLOW_SELECTED_PHOTOS_BUTTON = 12; + public static final int ALLOW_MORE_SELECTED_PHOTOS_BUTTON = 13; + public static final int DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON = 14; public static final int NEXT_LOCATION_DIALOG = 6; public static final int LOCATION_ACCURACY_LAYOUT = 0; @@ -106,13 +117,11 @@ public class GrantPermissionsActivity extends SettingsActivity public static final int DIALOG_WITH_FINE_LOCATION_ONLY = 4; public static final int DIALOG_WITH_COARSE_LOCATION_ONLY = 5; - public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT = - new HashMap<String, Integer>() {{ - put(ACCESS_COARSE_LOCATION, 0); - put(ACCESS_FINE_LOCATION, 1); - }}; + public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT = Map.of( + ACCESS_COARSE_LOCATION, 0, + ACCESS_FINE_LOCATION, 1); - private static final int APP_PERMISSION_REQUEST_CODE = 1; + public static final String INTENT_PHOTOS_SELECTED = "intent_extra_result"; /** * A map of the currently shown GrantPermissionsActivity for this user, per package and task ID @@ -197,7 +206,7 @@ public class GrantPermissionsActivity extends SettingsActivity // If this app is below the android T targetSdk, filter out the POST_NOTIFICATIONS // permission, if present mRequestedPermissions = GrantPermissionsViewModel.Companion - .filterNotificationPermissionIfNeededSync( + .filterPermissionsIfNeededSync( mTargetPackage, mRequestedPermissions); } @@ -380,6 +389,15 @@ public class GrantPermissionsActivity extends SettingsActivity if (info.getSendToSettingsImmediately()) { mViewModel.sendDirectlyToSettings(this, info.getGroupName()); return; + } else if (info.getOpenPhotoPicker()) { + mViewModel.openPhotoPicker(this, GRANTED_USER_SELECTED); + return; + } + + if (Utils.isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals( + info.getGroupName())) { + mViewModel.handleHealthConnectPermissions(this); + return; } CharSequence appLabel = KotlinUtils.INSTANCE.getPackageLabel(getApplication(), @@ -411,6 +429,9 @@ public class GrantPermissionsActivity extends SettingsActivity icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon); messageId = R.string.permgrouprequest_storage_pre_q; break; + case MORE_PHOTOS_MESSAGE: + messageId = R.string.permgrouprequest_more_photos; + break; } CharSequence message = getRequestMessage(appLabel, mTargetPackage, @@ -467,6 +488,16 @@ public class GrantPermissionsActivity extends SettingsActivity setTitle(message); } + CharSequence permissionRationaleMessage = null; + if (info.getShowPermissionRationale()) { + String permissionGroupLabel = + KotlinUtils.INSTANCE.getPermGroupLabel(this, info.getGroupName()) + .toString(); + + permissionRationaleMessage = getString(R.string.permission_rationale_message_template, + UCharacter.toLowerCase(permissionGroupLabel)); + } + ArrayList<Integer> idxs = new ArrayList<>(); mButtonVisibilities = new boolean[info.getButtonVisibilities().size()]; for (int i = 0; i < info.getButtonVisibilities().size(); i++) { @@ -482,7 +513,8 @@ public class GrantPermissionsActivity extends SettingsActivity } mViewHandler.updateUi(info.getGroupName(), mTotalRequests, mCurrentRequestIdx, icon, - message, detailMessage, mButtonVisibilities, mLocationVisibilities); + message, detailMessage, permissionRationaleMessage, mButtonVisibilities, + mLocationVisibilities); if (showingNewGroup) { mCurrentRequestIdx++; } @@ -543,30 +575,21 @@ public class GrantPermissionsActivity extends SettingsActivity protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Consumer<Intent> callback = mViewModel.getActivityResultCallback(); - - if (requestCode == APP_PERMISSION_REQUEST_CODE && callback != null) { - callback.accept(data); - mViewModel.setActivityResultCallback(null); + if (callback == null || (requestCode != APP_PERMISSION_REQUEST_CODE + && requestCode != PHOTO_PICKER_REQUEST_CODE)) { + return; } + if (requestCode == PHOTO_PICKER_REQUEST_CODE) { + data = new Intent("").putExtra(INTENT_PHOTOS_SELECTED, resultCode == RESULT_OK); + } + callback.accept(data); + mViewModel.setActivityResultCallback(null); } @Override public void onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result) { - if (checkKgm(name, null, result)) { - return; - } - - if (name == null || name.equals(mPreMergeShownGroupName)) { - mPreMergeShownGroupName = null; - } - - logGrantPermissionActivityButtons(name, null, result); - mViewModel.onPermissionGrantResult(name, null, result); - showNextRequest(); - if (result == CANCELED) { - setResultAndFinish(); - } + onPermissionGrantResult(name, null, result); } @Override @@ -576,10 +599,16 @@ public class GrantPermissionsActivity extends SettingsActivity return; } - if (name != null && name.equals(mPreMergeShownGroupName)) { + if (name == null || name.equals(mPreMergeShownGroupName)) { mPreMergeShownGroupName = null; } + if (Objects.equals(READ_MEDIA_VISUAL, name) + && result == GrantPermissionsViewHandler.GRANTED_USER_SELECTED) { + mViewModel.openPhotoPicker(this, result); + return; + } + logGrantPermissionActivityButtons(name, affectedForegroundPermissions, result); mViewModel.onPermissionGrantResult(name, affectedForegroundPermissions, result); showNextRequest(); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java index d5dc22e46..03f6d604c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java @@ -36,7 +36,7 @@ import java.util.List; public interface GrantPermissionsViewHandler { @Retention(SOURCE) @IntDef({CANCELED, GRANTED_ALWAYS, GRANTED_FOREGROUND_ONLY, DENIED, DENIED_DO_NOT_ASK_AGAIN, - GRANTED_ONE_TIME}) + GRANTED_ONE_TIME, GRANTED_USER_SELECTED, DENIED_MORE_PHOTOS}) @interface Result {} int LINKED_TO_SETTINGS = -2; int CANCELED = -1; @@ -45,6 +45,8 @@ public interface GrantPermissionsViewHandler { int DENIED = 2; int DENIED_DO_NOT_ASK_AGAIN = 3; int GRANTED_ONE_TIME = 4; + int GRANTED_USER_SELECTED = 5; + int DENIED_MORE_PHOTOS = 6; /** * Listener interface for getting notified when the user responds to a @@ -83,11 +85,15 @@ public interface GrantPermissionsViewHandler { * @param message the message to display the user * @param detailMessage another message to display to the user. This clarifies "message" in more * detail + * @param permissionRationaleMessage another message to display to the user. This message lets + * users know developer stated data usages for the requested + * permission * @param buttonVisibilities visibilities for each button * @param locationVisibilities visibilities for location options */ void updateUi(String groupName, int groupCount, int groupIndex, Icon icon, - CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, + CharSequence message, CharSequence detailMessage, + CharSequence permissionRationaleMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities); /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java index 91ca82338..c1472b02c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java @@ -16,6 +16,7 @@ package com.android.permissioncontroller.permission.ui; +import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.permissioncontroller.Constants.ACTION_MANAGE_AUTO_REVOKE; @@ -364,6 +365,13 @@ public final class ManagePermissionsActivity extends SettingsActivity { return; } + if (Utils.isHealthPermissionUiEnabled() && permissionGroupName + .equals(HEALTH_PERMISSION_GROUP)) { + Utils.navigateToHealthConnectSettings(this); + finishAfterTransition(); + return; + } + if (DeviceUtils.isAuto(this)) { androidXFragment = AutoPermissionAppsFragment.newInstance(permissionGroupName, sessionId); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java index 6dfceb90d..05a75f594 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java @@ -25,8 +25,8 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import com.android.permissioncontroller.DeviceUtils; -import com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt; import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageWrapperFragment; +import com.android.permissioncontroller.permission.utils.KotlinUtils; /** * A dialog listing the currently uses of camera, microphone, and location. @@ -40,8 +40,8 @@ public final class ReviewOngoingUsageActivity extends SettingsActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!DashboardUtilsKt.shouldShowCameraMicIndicators() - && !DashboardUtilsKt.shouldShowLocationIndicators()) { + if (!KotlinUtils.INSTANCE.shouldShowCameraMicIndicators() + && !KotlinUtils.INSTANCE.shouldShowLocationIndicators()) { finishAfterTransition(); return; } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java index 0973d1dfe..cf58f1e17 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java @@ -23,7 +23,6 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled; import static java.util.concurrent.TimeUnit.DAYS; @@ -146,7 +145,7 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment implem if (SdkLevel.isAtLeastS()) { mPermissionUsages = new PermissionUsages(getContext()); - long aggregateDataFilterBeginDays = is7DayToggleEnabled() + long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled() ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 : AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java index 194faff6f..6b09921cb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java @@ -91,8 +91,11 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand @Override public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon, - CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, + CharSequence message, CharSequence detailMessage, + CharSequence permissionRationaleMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities) { + // permissionRationaleMessage ignored by auto + mGroupName = groupName; mGroupCount = groupCount; mGroupIndex = groupIndex; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt index 0d2ee9895..ff7697cf9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt @@ -18,14 +18,13 @@ package com.android.permissioncontroller.permission.ui.auto.dashboard import android.content.Context import android.content.Intent +import android.text.format.DateFormat import androidx.preference.Preference.OnPreferenceClickListener import com.android.car.ui.preference.CarUiPreference import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel -/** - * Preference that displays a permission usage for an app. - */ +/** Preference that displays a permission usage for an app. */ class AutoPermissionHistoryPreference( context: Context, historyPreferenceData: PermissionUsageDetailsViewModel.HistoryPreferenceData @@ -33,21 +32,25 @@ class AutoPermissionHistoryPreference( init { title = historyPreferenceData.preferenceTitle - summary = if (historyPreferenceData.summaryText != null) { - context.getString(R.string.auto_permission_usage_timeline_summary, - historyPreferenceData.accessTime, historyPreferenceData.summaryText) - } else { - historyPreferenceData.accessTime - } + summary = + if (historyPreferenceData.summaryText != null) { + context.getString( + R.string.auto_permission_usage_timeline_summary, + DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime), + historyPreferenceData.summaryText) + } else { + DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime) + } if (historyPreferenceData.appIcon != null) { icon = historyPreferenceData.appIcon } onPreferenceClickListener = OnPreferenceClickListener { - val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply { - putExtra(Intent.EXTRA_USER, historyPreferenceData.userHandle) - putExtra(Intent.EXTRA_PACKAGE_NAME, historyPreferenceData.pkgName) - } + val intent = + Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply { + putExtra(Intent.EXTRA_USER, historyPreferenceData.userHandle) + putExtra(Intent.EXTRA_PACKAGE_NAME, historyPreferenceData.pkgName) + } context.startActivity(intent) true } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt index f13a3565d..1e336d905 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt @@ -17,14 +17,15 @@ package com.android.permissioncontroller.permission.ui.auto.dashboard import android.app.role.RoleManager -import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle +import android.text.format.DateFormat import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModelProvider import androidx.preference.Preference import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceScreen import com.android.car.ui.preference.CarUiPreference import com.android.permissioncontroller.Constants import com.android.permissioncontroller.DumpableLog @@ -34,47 +35,56 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED import com.android.permissioncontroller.R import com.android.permissioncontroller.auto.AutoSettingsFrameFragment +import com.android.permissioncontroller.permission.model.legacy.PermissionApps.AppDataLoader import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.model.v31.PermissionUsages import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback -import com.android.permissioncontroller.permission.model.legacy.PermissionApps.AppDataLoader -import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.auto.AutoDividerPreference import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionUsageEntry import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelFactory import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel import com.android.permissioncontroller.permission.utils.Utils -import java.util.concurrent.atomic.AtomicBoolean +import java.time.Clock +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.temporal.ChronoUnit import java.util.concurrent.atomic.AtomicReference @RequiresApi(Build.VERSION_CODES.S) -class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), - PermissionsUsagesChangeCallback { +class AutoPermissionUsageDetailsFragment : + AutoSettingsFrameFragment(), PermissionsUsagesChangeCallback { companion object { private const val LOG_TAG = "AutoPermissionUsageDetailsFragment" private const val KEY_SESSION_ID = "_session_id" private const val FILTER_24_HOURS = 2 + private val MIDNIGHT_TODAY = + ZonedDateTime.now(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS).toEpochSecond() * + 1000L + private val MIDNIGHT_YESTERDAY = + ZonedDateTime.now(ZoneId.systemDefault()) + .minusDays(1) + .truncatedTo(ChronoUnit.DAYS) + .toEpochSecond() * 1000L // Only show the last 24 hours on Auto right now private const val SHOW_7_DAYS = false - /** - * Creates a new instance of [AutoPermissionUsageDetailsFragment]. - */ + /** Creates a new instance of [AutoPermissionUsageDetailsFragment]. */ fun newInstance( groupName: String?, showSystem: Boolean, sessionId: Long ): AutoPermissionUsageDetailsFragment { return AutoPermissionUsageDetailsFragment().apply { - arguments = Bundle().apply { - putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) - putLong(Constants.EXTRA_SESSION_ID, sessionId) - putBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, showSystem) - } + arguments = + Bundle().apply { + putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) + putLong(Constants.EXTRA_SESSION_ID, sessionId) + putBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, showSystem) + } } } } @@ -91,7 +101,7 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), private var finishedInitialLoad = false private var hasSystemApps = false - /** Unique Id of a request */ + /** Unique Id of a request */ private var sessionId: Long = 0 override fun onCreate(savedInstanceState: Bundle?) { @@ -108,25 +118,26 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), return } filterGroup = requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME)!! - showSystem = requireArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, - false) - sessionId = savedInstanceState?.getLong(SESSION_ID_KEY) - ?: (arguments?.getLong(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID) - ?: Constants.INVALID_SESSION_ID) - headerLabel = resources.getString(R.string.permission_group_usage_title, - getPermGroupLabel(requireContext(), filterGroup)) + showSystem = + requireArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false) + sessionId = + savedInstanceState?.getLong(SESSION_ID_KEY) + ?: (arguments?.getLong(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID) + ?: Constants.INVALID_SESSION_ID) + headerLabel = + resources.getString( + R.string.permission_group_usage_title, + getPermGroupLabel(requireContext(), filterGroup)) val context = preferenceManager.getContext() - permissionUsages = - PermissionUsages( - context - ) + permissionUsages = PermissionUsages(context) roleManager = Utils.getSystemServiceSafe(context, RoleManager::class.java) - val usageViewModelFactory = PermissionUsageDetailsViewModelFactory( - PermissionControllerApplication.get(), roleManager, - filterGroup, sessionId) - usageViewModel = ViewModelProvider(this, - usageViewModelFactory)[PermissionUsageDetailsViewModel::class.java] + val usageViewModelFactory = + PermissionUsageDetailsViewModelFactory( + PermissionControllerApplication.get(), roleManager, filterGroup, sessionId) + usageViewModel = + ViewModelProvider(this, usageViewModelFactory)[ + PermissionUsageDetailsViewModel::class.java] reloadData() } @@ -142,9 +153,7 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), preferenceScreen.addPreference(AutoDividerPreference(context)) } - /** - * Reloads the data to show. - */ + /** Reloads the data to show. */ private fun reloadData() { usageViewModel.loadPermissionUsages( requireActivity().getLoaderManager(), permissionUsages, this, FILTER_24_HOURS) @@ -164,7 +173,8 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), private fun updateSystemToggle() { if (!showSystem) { PermissionControllerStatsLog.write( - PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, + PERMISSION_USAGE_FRAGMENT_INTERACTION, + sessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED) } showSystem = !showSystem @@ -177,11 +187,12 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), setAction(null, null) return } - val label = if (showSystem) { - getString(R.string.menu_hide_system) - } else { - getString(R.string.menu_show_system) - } + val label = + if (showSystem) { + getString(R.string.menu_hide_system) + } else { + getString(R.string.menu_show_system) + } setAction(label) { updateSystemToggle() } } @@ -192,68 +203,102 @@ class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment(), preferenceScreen.removeAll() setupHeaderPreferences() - val permApps = arrayListOf<PermissionApp>() val exemptedPackages = Utils.getExemptedPackages(roleManager) - val seenSystemApp = AtomicBoolean(false) - val usages: List<AppPermissionUsageEntry> = usageViewModel.parseUsages( - appPermissionUsages, exemptedPackages, permApps, seenSystemApp, showSystem, - SHOW_7_DAYS) - if (hasSystemApps != seenSystemApp.get()) { - hasSystemApps = seenSystemApp.get() + val uiData = + usageViewModel.buildPermissionUsageDetailsUiData( + appPermissionUsages, showSystem, SHOW_7_DAYS) + + if (hasSystemApps != uiData.shouldDisplayShowSystemToggle) { + hasSystemApps = uiData.shouldDisplayShowSystemToggle updateAction() } - val preferenceFactory = PreferenceFactory(requireActivity()) - val category = AtomicReference(preferenceFactory.createDayCategoryPreference()) + val category = AtomicReference(PreferenceCategory(context)) preferenceScreen.addPreference(category.get()) AppDataLoader(context) { - usageViewModel.renderTimelinePreferences(usages, category, preferenceScreen, - preferenceFactory) + renderHistoryPreferences( + uiData.getHistoryPreferenceDataList(), category, preferenceScreen) - setLoading(false) - finishedInitialLoad = true - permissionUsages.stopLoader(requireActivity().getLoaderManager()) - }.execute(*permApps.toTypedArray()) + setLoading(false) + finishedInitialLoad = true + permissionUsages.stopLoader(requireActivity().getLoaderManager()) + } + .execute(*uiData.permissionApps.toTypedArray()) } - private class PreferenceFactory(val context: Context) : - PermissionUsageDetailsViewModel.HistoryPreferenceFactory { - - override fun createDayCategoryPreference(): PreferenceCategory { - return PreferenceCategory(context) - } - - override fun createPermissionHistoryPreference( - historyPreferenceData: PermissionUsageDetailsViewModel.HistoryPreferenceData - ): Preference { - return AutoPermissionHistoryPreference(context, historyPreferenceData) - } + fun createPermissionHistoryPreference( + historyPreferenceData: PermissionUsageDetailsViewModel.HistoryPreferenceData + ): Preference { + return AutoPermissionHistoryPreference(requireContext(), historyPreferenceData) } private fun addTimelineDescriptionPreference() { - val preference = CarUiPreference(context).apply { - summary = getString(R.string.permission_group_usage_subtitle_24h, - getPermGroupLabel(requireContext(), filterGroup)) - isSelectable = false - } + val preference = + CarUiPreference(context).apply { + summary = + getString( + R.string.permission_group_usage_subtitle_24h, + getPermGroupLabel(requireContext(), filterGroup)) + isSelectable = false + } preferenceScreen.addPreference(preference) } private fun addManagePermissionPreference() { - val preference = CarUiPreference(context).apply { - title = getString(R.string.manage_permission) - summary = getString(R.string.manage_permission_summary, - getPermGroupLabel(requireContext(), filterGroup)) - onPreferenceClickListener = Preference.OnPreferenceClickListener { - val intent = Intent(Intent.ACTION_MANAGE_PERMISSION_APPS).apply { - putExtra(Intent.EXTRA_PERMISSION_NAME, filterGroup) + val preference = + CarUiPreference(context).apply { + title = getString(R.string.manage_permission) + summary = + getString( + R.string.manage_permission_summary, + getPermGroupLabel(requireContext(), filterGroup)) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { + val intent = + Intent(Intent.ACTION_MANAGE_PERMISSION_APPS).apply { + putExtra(Intent.EXTRA_PERMISSION_NAME, filterGroup) + } + startActivity(intent) + true + } + } + preferenceScreen.addPreference(preference) + } + + /** Render the provided [historyPreferenceDataList] into the [preferenceScreen] UI. */ + fun renderHistoryPreferences( + historyPreferenceDataList: List<PermissionUsageDetailsViewModel.HistoryPreferenceData>, + category: AtomicReference<PreferenceCategory>, + preferenceScreen: PreferenceScreen, + ) { + var previousDateMs = 0L + historyPreferenceDataList.forEach { + val usageTimestamp = it.accessEndTime + val currentDateMs = + ZonedDateTime.ofInstant( + Instant.ofEpochMilli(usageTimestamp), + Clock.system(ZoneId.systemDefault()).zone) + .truncatedTo(ChronoUnit.DAYS) + .toEpochSecond() * 1000L + if (currentDateMs != previousDateMs) { + if (previousDateMs != 0L) { + category.set(PreferenceCategory(context)) + preferenceScreen.addPreference(category.get()) } - startActivity(intent) - true + if (usageTimestamp > MIDNIGHT_TODAY) { + category.get().setTitle(R.string.permission_history_category_today) + } else if (usageTimestamp > MIDNIGHT_YESTERDAY) { + category.get().setTitle(R.string.permission_history_category_yesterday) + } else { + category + .get() + .setTitle(DateFormat.getDateFormat(context).format(currentDateMs)) + } + previousDateMs = currentDateMs } + category.get().addPreference(createPermissionHistoryPreference(it)) } - preferenceScreen.addPreference(preference) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt index 323a13706..a4d1398b0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt @@ -31,15 +31,16 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED import com.android.permissioncontroller.R import com.android.permissioncontroller.auto.AutoSettingsFrameFragment -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage -import com.android.permissioncontroller.permission.model.v31.PermissionUsages -import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.model.legacy.PermissionApps.AppDataLoader import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage +import com.android.permissioncontroller.permission.model.v31.PermissionUsages +import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel.PermissionGroupWithUsageCount import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelFactory import com.android.permissioncontroller.permission.utils.Utils @@ -67,33 +68,32 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag private var finishedInitialLoad = false private var hasSystemApps = false - /** Unique Id of a request */ + /** Unique Id of a request */ private var sessionId: Long = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) headerLabel = getString(R.string.permission_usage_title) - sessionId = savedInstanceState?.getLong(SESSION_ID_KEY) - ?: (arguments?.getLong(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID) - ?: Constants.INVALID_SESSION_ID) + sessionId = + savedInstanceState?.getLong(SESSION_ID_KEY) + ?: (arguments?.getLong(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID) + ?: Constants.INVALID_SESSION_ID) val context: Context = preferenceManager.getContext() - permissionUsages = - PermissionUsages( - context - ) + permissionUsages = PermissionUsages(context) val roleManager = Utils.getSystemServiceSafe(context, RoleManager::class.java) val application: Application = requireActivity().getApplication() - val managePermissionsViewModelFactory = ViewModelProvider.AndroidViewModelFactory - .getInstance(application) - managePermissionsViewModel = ViewModelProvider(this, - managePermissionsViewModelFactory)[ManagePermissionsViewModel::class.java] + val managePermissionsViewModelFactory = + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + managePermissionsViewModel = + ViewModelProvider(this, managePermissionsViewModelFactory)[ + ManagePermissionsViewModel::class.java] val usageViewModelFactory = PermissionUsageViewModelFactory(roleManager) - usageViewModel = ViewModelProvider(this, - usageViewModelFactory)[PermissionUsageViewModel::class.java] + usageViewModel = + ViewModelProvider(this, usageViewModelFactory)[PermissionUsageViewModel::class.java] - managePermissionsViewModel.standardPermGroupsLiveData.observe(this, - this::onPermissionGroupsChanged) + managePermissionsViewModel.standardPermGroupsLiveData.observe( + this, this::onPermissionGroupsChanged) setLoading(true) reloadData() } @@ -114,7 +114,9 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag private fun updateSystemToggle() { if (!showSystem) { - PermissionControllerStatsLog.write(PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, + PermissionControllerStatsLog.write( + PERMISSION_USAGE_FRAGMENT_INTERACTION, + sessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED) } showSystem = !showSystem @@ -127,17 +129,16 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag setAction(null, null) return } - val label = if (showSystem) { - getString(R.string.menu_hide_system) - } else { - getString(R.string.menu_show_system) - } + val label = + if (showSystem) { + getString(R.string.menu_hide_system) + } else { + getString(R.string.menu_show_system) + } setAction(label) { updateSystemToggle() } } - /** - * Reloads the data to show. - */ + /** Reloads the data to show. */ private fun reloadData() { usageViewModel.loadPermissionUsages( requireActivity().getLoaderManager(), permissionUsages, this) @@ -160,56 +161,67 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag } getPreferenceScreen().removeAll() - val (usages, permApps, seenSystemApps) = usageViewModel.extractUsages(appPermissionUsages, - show7Days, showSystem) + val permissionUsagesUiData = + usageViewModel.buildPermissionUsagesUiData( + appPermissionUsages, show7Days, showSystem, requireContext()) + val permissionApps = permissionUsagesUiData.permissionApps + val displayShowSystemToggle = permissionUsagesUiData.displayShowSystemToggle - if (hasSystemApps != seenSystemApps) { - hasSystemApps = seenSystemApps + if (hasSystemApps != displayShowSystemToggle) { + hasSystemApps = displayShowSystemToggle updateAction() } - val groupUsagesList: List<Map.Entry<String, Int>> = usageViewModel - .createGroupUsagesList(requireContext(), usages) + val permissionGroupWithUsageCounts: List<PermissionGroupWithUsageCount> = + permissionUsagesUiData.orderedPermissionGroupsWithUsageCount - addUIContent(groupUsagesList, permApps) + addUIContent(permissionGroupWithUsageCounts, permissionApps) } - /** - * Use the usages and permApps that are previously constructed to add UI content to the page - */ + /** Use the usages and permApps that are previously constructed to add UI content to the page */ private fun addUIContent( - usages: List<Map.Entry<String, Int>>, + permissionGroupWithUsageCounts: + List<PermissionGroupWithUsageCount>, permApps: java.util.ArrayList<PermissionApp> ) { AppDataLoader(context) { - // Show permission groups with permissions granted to an app, including groups - // where the permission is only granted to a system app. This still excludes groups - // that don't have grants from any apps. Showing the same groups regardless of - // whether showSystem is selected avoids permission groups hiding and appearing, - // which is a confusing user experience. - val usedPermissionGroups = permissionGroups - .filter { - (it.nonSystemUserSetOrPreGranted > 0) or - (it.systemUserSetOrPreGranted > 0) + // Show permission groups with permissions granted to an app, including groups + // where the permission is only granted to a system app. This still excludes groups + // that don't have grants from any apps. Showing the same groups regardless of + // whether showSystem is selected avoids permission groups hiding and appearing, + // which is a confusing user experience. + val usedPermissionGroups = + permissionGroups + .filter { + (it.nonSystemUserSetOrPreGranted > 0) or + (it.systemUserSetOrPreGranted > 0) + } + .filterNot { it.onlyShellPackageGranted } + + for (i in permissionGroupWithUsageCounts.indices) { + val groupName = permissionGroupWithUsageCounts[i].permGroup + val count = permissionGroupWithUsageCounts[i].appCount + if ((usedPermissionGroups.filter { it.name == groupName }).isEmpty()) { + continue + } + val permissionUsagePreference = CarUiPreference(requireContext()) + PermissionUsageControlPreferenceUtils.initPreference( + permissionUsagePreference, + requireContext(), + groupName, + count, + showSystem, + sessionId, + show7Days) + getPreferenceScreen().addPreference(permissionUsagePreference) } - .filterNot { it.onlyShellPackageGranted } - - for (i in usages.indices) { - val (groupName, count) = usages[i] - if ((usedPermissionGroups.filter { it.name == groupName }).isEmpty()) { - continue + finishedInitialLoad = true + setLoading(false) + val activity: Activity? = activity + if (activity != null) { + permissionUsages.stopLoader(activity.loaderManager) } - val permissionUsagePreference = CarUiPreference(requireContext()) - PermissionUsageControlPreferenceUtils.initPreference(permissionUsagePreference, - requireContext(), groupName, count, showSystem, sessionId, show7Days) - getPreferenceScreen().addPreference(permissionUsagePreference) - } - finishedInitialLoad = true - setLoading(false) - val activity: Activity? = activity - if (activity != null) { - permissionUsages.stopLoader(activity.loaderManager) } - }.execute(*permApps.toTypedArray()) + .execute(*permApps.toTypedArray()) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java index eaa45fc8c..6821b0cd7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.handheld; import static android.Manifest.permission_group.STORAGE; +import static android.app.Activity.RESULT_OK; import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; @@ -27,6 +28,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; @@ -38,7 +40,6 @@ import static com.android.permissioncontroller.permission.ui.ManagePermissionsAc import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; import android.app.ActionBar; -import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.role.RoleManager; @@ -109,6 +110,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private @NonNull RadioButton mAllowForegroundButton; private @NonNull RadioButton mAskOneTimeButton; private @NonNull RadioButton mAskButton; + private @NonNull RadioButton mSelectButton; private @NonNull RadioButton mDenyButton; private @NonNull RadioButton mDenyForegroundButton; private @NonNull View mLocationAccuracy; @@ -203,6 +205,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader if (mIsStorageGroup) { mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState); } + mViewModel.registerPhotoPickerResultIfNeeded(this); mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class); } @@ -251,6 +254,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button); mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button); mAskButton = root.requireViewById(R.id.ask_radio_button); + mSelectButton = root.requireViewById(R.id.select_photos_radio_button); mDenyButton = root.requireViewById(R.id.deny_radio_button); mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button); mDivider = root.requireViewById(R.id.two_target_divider); @@ -378,8 +382,20 @@ public class AppPermissionFragment extends SettingsWithLargeHeader APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME); setResult(DENIED); }); + mSelectButton.setOnClickListener((v) -> { + int buttonPressed = + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED; + mViewModel.openPhotoPicker(result -> { + if (result == RESULT_OK) { + mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED, + buttonPressed); + } else { + // Reset the button state to what is was previously + setRadioButtonsState(states); + } + }); + }); mDenyButton.setOnClickListener((v) -> { - if (mViewModel.getFullStorageStateLiveData().getValue() != null && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) { mViewModel.setAllFilesAccess(false); @@ -416,6 +432,12 @@ public class AppPermissionFragment extends SettingsWithLargeHeader setButtonState(mAskButton, states.get(ButtonType.ASK)); setButtonState(mDenyButton, states.get(ButtonType.DENY)); setButtonState(mDenyForegroundButton, states.get(ButtonType.DENY_FOREGROUND)); + setButtonState(mSelectButton, states.get(ButtonType.SELECT_PHOTOS)); + if (mSelectButton.getVisibility() == View.VISIBLE) { + mAllowButton.setText(R.string.app_permission_button_allow_all_photos); + } else { + mAllowButton.setText(R.string.app_permission_button_allow); + } ButtonState locationAccuracyState = states.get(ButtonType.LOCATION_ACCURACY); if (!locationAccuracyState.isShown()) { @@ -485,7 +507,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader Intent intent = new Intent() .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED, mPermGroupName) .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result); - getActivity().setResult(Activity.RESULT_OK, intent); + getActivity().setResult(RESULT_OK, intent); } private void setDetail(Pair<Integer, Integer> detailResIds) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java index 9bb49bed6..568d7f225 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java @@ -24,7 +24,6 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED; import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled; import static java.util.concurrent.TimeUnit.DAYS; @@ -183,7 +182,7 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i Context context = getPreferenceManager().getContext(); mPermissionUsages = new PermissionUsages(context); - long aggregateDataFilterBeginDays = is7DayToggleEnabled() + long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled() ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 : AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt index 5d1d29df5..e39a8fbad 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt @@ -19,17 +19,7 @@ package com.android.permissioncontroller.permission.ui.handheld import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.app.Activity -import android.content.res.Configuration -import android.graphics.Color -import android.graphics.ImageDecoder -import android.graphics.Paint -import android.graphics.Path -import android.graphics.PixelFormat -import android.graphics.PorterDuff -import android.graphics.PorterDuffXfermode import android.graphics.Typeface -import android.graphics.drawable.AnimatedImageDrawable -import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.os.Bundle import android.text.method.LinkMovementMethod @@ -50,18 +40,25 @@ import android.widget.LinearLayout import android.widget.RadioButton import android.widget.RadioGroup import android.widget.TextView +import androidx.annotation.RawRes +import com.airbnb.lottie.LottieCompositionFactory +import com.airbnb.lottie.LottieDrawable import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_MORE_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON @@ -74,9 +71,11 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandle import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE_PHOTOS import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.ResultListener class GrantPermissionsViewHandlerImpl( @@ -87,7 +86,7 @@ class GrantPermissionsViewHandlerImpl( private val LOCATION_ACCURACY_DIALOGS = listOf(DIALOG_WITH_BOTH_LOCATIONS, DIALOG_WITH_FINE_LOCATION_ONLY, DIALOG_WITH_COARSE_LOCATION_ONLY) private val LOCATION_ACCURACY_IMAGE_DIAMETER = mActivity.resources.getDimension( - R.dimen.location_accuracy_image_size).toInt() + R.dimen.location_accuracy_image_size) // Configuration of the current dialog private var groupName: String? = null @@ -96,21 +95,24 @@ class GrantPermissionsViewHandlerImpl( private var groupIcon: Icon? = null private var groupMessage: CharSequence? = null private var detailMessage: CharSequence? = null + private var permissionRationaleMessage: CharSequence? = null private val buttonVisibilities = BooleanArray(NEXT_BUTTON) { false } private val locationVisibilities = BooleanArray(NEXT_LOCATION_DIALOG) { false } private var selectedPrecision: Int = 0 private var isLocationPermissionDialogActionClicked: Boolean = false private var coarseRadioButton: RadioButton? = null private var fineRadioButton: RadioButton? = null - private var coarseOffDrawable: AnimatedImageDrawable? = null - private var coarseOnDrawable: AnimatedImageDrawable? = null - private var fineOffDrawable: AnimatedImageDrawable? = null - private var fineOnDrawable: AnimatedImageDrawable? = null + private var coarseOffDrawable: LottieDrawable? = null + private var coarseOnDrawable: LottieDrawable? = null + private var fineOffDrawable: LottieDrawable? = null + private var fineOnDrawable: LottieDrawable? = null // Views private var iconView: ImageView? = null private var messageView: TextView? = null private var detailMessageView: TextView? = null + private var permissionRationaleView: View? = null + private var permissionRationaleMessageView: TextView? = null private var buttons: Array<Button?> = arrayOfNulls(NEXT_BUTTON) private var locationViews: Array<View?> = arrayOfNulls(NEXT_LOCATION_DIALOG) private var rootView: ViewGroup? = null @@ -122,6 +124,8 @@ class GrantPermissionsViewHandlerImpl( arguments.putParcelable(ARG_GROUP_ICON, groupIcon) arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage) arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage) + arguments.putCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE, + permissionRationaleMessage) arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities) arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities) arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision) @@ -134,6 +138,8 @@ class GrantPermissionsViewHandlerImpl( groupCount = savedInstanceState.getInt(ARG_GROUP_COUNT) groupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX) detailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE) + permissionRationaleMessage = + savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE) setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES)) setLocationVisibilities(savedInstanceState.getBooleanArray( ARG_DIALOG_LOCATION_VISIBILITIES)) @@ -143,14 +149,15 @@ class GrantPermissionsViewHandlerImpl( } override fun updateUi( - groupName: String, + groupName: String?, groupCount: Int, groupIndex: Int, icon: Icon?, message: CharSequence?, detailMessage: CharSequence?, - buttonVisibilities: BooleanArray, - locationVisibilities: BooleanArray + permissionRationaleMessage: CharSequence?, + buttonVisibilities: BooleanArray?, + locationVisibilities: BooleanArray? ) { this.groupName = groupName @@ -159,6 +166,7 @@ class GrantPermissionsViewHandlerImpl( groupIcon = icon groupMessage = message this.detailMessage = detailMessage + this.permissionRationaleMessage = permissionRationaleMessage setButtonVisibilities(buttonVisibilities) setLocationVisibilities(locationVisibilities) @@ -171,11 +179,12 @@ class GrantPermissionsViewHandlerImpl( private fun updateAll() { updateDescription() updateDetailDescription() + updatePermissionRationale() updateButtons() updateLocationVisibilities() - // Animate change in size - // Grow or shrink the content container to size of new content + // Animate change in size + // Grow or shrink the content container to size of new content val growShrinkToNewContentSize = ChangeBounds() growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity, @@ -213,6 +222,10 @@ class GrantPermissionsViewHandlerImpl( detailMessageView!!.movementMethod = LinkMovementMethod.getInstance() iconView = rootView.findViewById(R.id.permission_icon) + permissionRationaleView = rootView.findViewById(R.id.permission_rationale_container) + permissionRationaleMessageView = rootView.findViewById(R.id.permission_rationale_message) + permissionRationaleView!!.setOnClickListener(this) + val buttons = arrayOfNulls<Button>(NEXT_BUTTON) val numButtons = BUTTON_RES_ID_TO_NUM.size() for (i in 0 until numButtons) { @@ -227,6 +240,7 @@ class GrantPermissionsViewHandlerImpl( val locationView = rootView.findViewById<View>(LOCATION_RES_ID_TO_NUM.keyAt(i)) locationViews[LOCATION_RES_ID_TO_NUM.valueAt(i)] = locationView } + initializeAnimatedImages() // Set location accuracy radio buttons' click listeners @@ -243,44 +257,26 @@ class GrantPermissionsViewHandlerImpl( return rootView } - private fun initializeAnimatedImages() { - val isDarkMode = (mActivity.resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES - val coarseOffDrawableId = if (isDarkMode) R.drawable.coarse_off_dark - else R.drawable.coarse_off_light - val coarseOnDrawableId = if (isDarkMode) R.drawable.coarse_on_dark - else R.drawable.coarse_on_light - val fineOffDrawableId = if (isDarkMode) R.drawable.fine_off_dark - else R.drawable.fine_off_light - val fineOnDrawableId = if (isDarkMode) R.drawable.fine_on_dark else R.drawable.fine_on_light - - coarseOffDrawable = getDrawableFromId(coarseOffDrawableId) as AnimatedImageDrawable - coarseOnDrawable = getDrawableFromId(coarseOnDrawableId) as AnimatedImageDrawable - fineOffDrawable = getDrawableFromId(fineOffDrawableId) as AnimatedImageDrawable - fineOnDrawable = getDrawableFromId(fineOnDrawableId) as AnimatedImageDrawable - } - - private fun getDrawableFromId(drawableId: Int): Drawable { - val source = ImageDecoder.createSource(mActivity.resources, drawableId) - return ImageDecoder.decodeDrawable(source) { decoder, _, _ -> - decoder.setTargetSize(LOCATION_ACCURACY_IMAGE_DIAMETER, - LOCATION_ACCURACY_IMAGE_DIAMETER) - decoder.setPostProcessor { canvas -> - // This will crop the image to circle image. - val path = Path() - path.fillType = Path.FillType.INVERSE_EVEN_ODD - val width: Int = canvas.width - val height: Int = canvas.height - path.addRoundRect(0f, 0f, width.toFloat(), height.toFloat(), - width.toFloat() / 2, height.toFloat() / 2, Path.Direction.CW) - val paint = Paint() - paint.isAntiAlias = true - paint.color = Color.TRANSPARENT - paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC) - canvas.drawPath(path, paint) - PixelFormat.TRANSLUCENT + private fun getLottieDrawable(@RawRes rawResId: Int): LottieDrawable { + val composition = LottieCompositionFactory.fromRawResSync(mActivity, rawResId).value!! + val scale = LOCATION_ACCURACY_IMAGE_DIAMETER / composition.bounds.width() + val drawable = object : LottieDrawable() { + override fun getIntrinsicHeight(): Int { + return (super.getIntrinsicHeight() * scale).toInt() + } + override fun getIntrinsicWidth(): Int { + return (super.getIntrinsicWidth() * scale).toInt() } } + drawable.composition = composition + return drawable + } + + private fun initializeAnimatedImages() { + coarseOffDrawable = getLottieDrawable(R.raw.coarse_loc_off) + coarseOnDrawable = getLottieDrawable(R.raw.coarse_loc_on) + fineOffDrawable = getLottieDrawable(R.raw.fine_loc_off) + fineOnDrawable = getLottieDrawable(R.raw.fine_loc_on) } override fun updateWindowAttributes(outLayoutParams: LayoutParams) { @@ -323,6 +319,16 @@ class GrantPermissionsViewHandlerImpl( } } + private fun updatePermissionRationale() { + val message = permissionRationaleMessage + if (message == null || message.isEmpty()) { + permissionRationaleView!!.visibility = View.GONE + } else { + permissionRationaleMessageView!!.text = message + permissionRationaleView!!.visibility = View.VISIBLE + } + } + private fun updateButtons() { for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) { val pos = BUTTON_RES_ID_TO_NUM.valueAt(i) @@ -420,8 +426,8 @@ class GrantPermissionsViewHandlerImpl( null, null) fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOffDrawable, null, null) - coarseOnDrawable?.start() fineOffDrawable?.start() + coarseOnDrawable?.start() coarseRadioButton?.setTypeface(null, Typeface.BOLD) fineRadioButton?.setTypeface(null, Typeface.NORMAL) } @@ -430,6 +436,11 @@ class GrantPermissionsViewHandlerImpl( override fun onClick(view: View) { val id = view.id + if (id == R.id.permission_rationale_container) { + // TODO(b/256913489): Implement Permission rationale details activity + return + } + if (id == R.id.permission_location_accuracy_radio_fine) { (locationViews[FINE_RADIO_BUTTON] as RadioButton).isChecked = true selectedPrecision = FINE_RADIO_BUTTON @@ -469,6 +480,7 @@ class GrantPermissionsViewHandlerImpl( } when (BUTTON_RES_ID_TO_NUM.get(id, -1)) { + ALLOW_ALL_PHOTOS_BUTTON, ALLOW_BUTTON -> { view.performAccessibilityAction( AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) @@ -493,6 +505,16 @@ class GrantPermissionsViewHandlerImpl( resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, GRANTED_ONE_TIME) } + ALLOW_SELECTED_PHOTOS_BUTTON, ALLOW_MORE_SELECTED_PHOTOS_BUTTON -> { + view.performAccessibilityAction( + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) + resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, + GRANTED_USER_SELECTED) + } + DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON -> { + resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, + DENIED_MORE_PHOTOS) + } DENY_BUTTON, NO_UPGRADE_BUTTON, NO_UPGRADE_OT_BUTTON -> { view.performAccessibilityAction( AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) @@ -522,6 +544,7 @@ class GrantPermissionsViewHandlerImpl( } companion object { + private val TAG = GrantPermissionsViewHandlerImpl::class.java.simpleName const val ARG_GROUP_NAME = "ARG_GROUP_NAME" const val ARG_GROUP_COUNT = "ARG_GROUP_COUNT" @@ -529,6 +552,8 @@ class GrantPermissionsViewHandlerImpl( const val ARG_GROUP_ICON = "ARG_GROUP_ICON" const val ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE" private const val ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE" + private const val ARG_GROUP_PERMISSION_RATIONALE_MESSAGE = + "ARG_GROUP_PERMISSION_RATIONALE_MESSAGE" private const val ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES" private const val ARG_DIALOG_LOCATION_VISIBILITIES = "ARG_DIALOG_LOCATION_VISIBILITIES" private const val ARG_DIALOG_SELECTED_PRECISION = "ARG_DIALOG_SELECTED_PRECISION" @@ -557,6 +582,14 @@ class GrantPermissionsViewHandlerImpl( NO_UPGRADE_OT_BUTTON) BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_photos_button, + ALLOW_ALL_PHOTOS_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_photos_button, + ALLOW_SELECTED_PHOTOS_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_more_selected_photos_button, + ALLOW_MORE_SELECTED_PHOTOS_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_dont_allow_more_selected_photos_button, + DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON) LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT) LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_fine, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java index 879e6082a..8e3192eee 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java @@ -20,7 +20,6 @@ import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory; import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.shouldShowPermissionsDashboard; import android.app.Application; import android.content.Intent; @@ -39,6 +38,7 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; import com.android.permissioncontroller.permission.ui.UnusedAppsFragment; import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel; +import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.StringUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.widget.FooterPreference; @@ -77,6 +77,9 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr mPermissionGroups = mViewModel.getUiDataLiveData().getValue(); mViewModel.getUiDataLiveData().observe(this, permissionGroups -> { + // Once we have loaded data for the first time, further loads should be staggered, + // for performance reasons. + mViewModel.getUiDataLiveData().setLoadStaggered(true); if (permissionGroups != null) { mPermissionGroups = permissionGroups; updatePermissionsUi(); @@ -84,6 +87,11 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr Log.e(LOG_TAG, "ViewModel returned null data, exiting"); getActivity().finishAfterTransition(); } + + // If we've loaded all LiveDatas, no need to prioritize loading any particular one + if (!mViewModel.getUiDataLiveData().isStale()) { + mViewModel.getUiDataLiveData().setFirstLoadGroup(null); + } }); mViewModel.getNumCustomPermGroups().observe(this, permNames -> updatePermissionsUi()); @@ -115,7 +123,7 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - if (shouldShowPermissionsDashboard()) { + if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) { menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title); } } @@ -209,6 +217,9 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr @Override public void showPermissionApps(String permissionGroupName) { + // If we return to this page within a reasonable time, prioritize loading data from the + // permission group whose page we are going to, as that is group most likely to have changed + mViewModel.getUiDataLiveData().setFirstLoadGroup(permissionGroupName); mViewModel.showPermissionApps(this, PermissionAppsFragment.createArgs( permissionGroupName, getArguments().getLong(EXTRA_SESSION_ID))); } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java index 1d92a6e74..90d7204cf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java @@ -22,7 +22,6 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FO import static com.android.permissioncontroller.permission.ui.Category.ASK; import static com.android.permissioncontroller.permission.ui.Category.DENIED; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.shouldShowPermissionsDashboard; import android.Manifest; import android.app.ActionBar; @@ -197,7 +196,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem updateMenu(mViewModel.getShouldShowSystemLiveData().getValue()); } - if (shouldShowPermissionsDashboard()) { + if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) { menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title); } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java index 31ef791b6..814adf6ee 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java @@ -16,6 +16,8 @@ package com.android.permissioncontroller.permission.ui.handheld; +import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP; + import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.GRANT_CATEGORY; @@ -221,6 +223,11 @@ public class PermissionControlPreference extends Preference { Utils.navigateToAppNotificationSettings(mContext, mPackageName, mUser); return true; } + if (Utils.isHealthPermissionUiEnabled() + && mPermGroupName.equals(HEALTH_PERMISSION_GROUP)) { + Utils.navigateToAppHealthConnectSettings(mContext, mPackageName, mUser); + return true; + } Bundle args = new Bundle(); args.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName); args.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java index 9569baeeb..658a82af5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java @@ -36,10 +36,10 @@ import androidx.preference.PreferenceFragmentCompat; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionSummary; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.SummaryMessage; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionSummary; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.SummaryMessage; import com.android.permissioncontroller.permission.utils.LocationUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.RestrictedLockUtils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java index a6f74c822..5e5c221ae 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java @@ -57,9 +57,9 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup; import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionViewModelFactory; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel; -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionViewModelFactory; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel; +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt index 19b19f620..5b92dd36d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt @@ -19,7 +19,6 @@ package com.android.permissioncontroller.permission.ui.handheld.v31 import android.content.Context import android.icu.util.Calendar import android.os.Build -import android.provider.DeviceConfig import android.text.format.DateFormat.getMediumDateFormat import android.text.format.DateFormat.getTimeFormat import android.util.Pair @@ -30,58 +29,12 @@ import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage. import com.android.permissioncontroller.permission.utils.StringUtils import java.util.Locale -/** Whether to show the Permissions Hub. */ -private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled" - -/** Whether to show the mic and camera icons. */ -const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled" - -/** Whether to show the location indicators. */ -const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled" - -/* Whether location accuracy feature is enabled */ -const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled" - -/** Whether to show 7-day toggle in privacy hub. */ -private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle" - -/* Default location precision */ -const val PROPERTY_LOCATION_PRECISION = "location_precision" - const val SECONDS = 1 const val MINUTES = 2 const val HOURS = 3 const val DAYS = 4 /** - * Whether the Permissions Hub 2 flag is enabled - * - * @return whether the flag is enabled - */ -fun isPermissionsHub2FlagEnabled(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_PERMISSIONS_HUB_2_ENABLED, false) -} -/** - * Whether to show the Permissions Dashboard - * - * @return whether to show the Permissions Dashboard. - */ -fun shouldShowPermissionsDashboard(): Boolean { - return isPermissionsHub2FlagEnabled() -} - -/** - * Whether we should enable the 7-day toggle in privacy dashboard - * - * @return whether the flag is enabled - */ -fun is7DayToggleEnabled(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PRIVACY_DASHBOARD_7_DAY_TOGGLE, false) -} - -/** * Whether to show the subattribution in the Permissions Dashboard * * @return whether to show subattribution in the Permissions Dashboard. @@ -91,62 +44,6 @@ fun shouldShowSubattributionInPermissionsDashboard(): Boolean { } /** - * Whether the Camera and Mic Icons are enabled by flag. - * - * @return whether the Camera and Mic Icons are enabled. - */ -fun isCameraMicIconsFlagEnabled(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) -} - -/** - * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons - * specifically, are enabled. - * - * @return whether to show the icons. - */ -fun shouldShowCameraMicIndicators(): Boolean { - return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled() -} - -/** - * Whether the location indicators are enabled by flag. - * - * @return whether the location indicators are enabled by flag. - */ -fun isLocationIndicatorsFlagEnabled(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_INDICATORS_ENABLED, false) -} - -/** - * Whether to show the location indicators. The location indicators are enable if the - * permission hub, or location indicator specifically are enabled. - */ -fun shouldShowLocationIndicators(): Boolean { - return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled() -} - -/** - * Whether the location accuracy feature is enabled - */ -fun isLocationAccuracyEnabled(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_ACCURACY_ENABLED, true) -} - -/** - * Default state of location precision - * true: default is FINE. - * false: default is COARSE. - */ -fun getDefaultPrecision(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_PRECISION, true) -} - -/** * Build a string representing the given time if it happened on the current day and the date * otherwise. * diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java index 9556d1590..5b24bc316 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java @@ -18,7 +18,6 @@ package com.android.permissioncontroller.permission.ui.handheld.v31; import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled; import android.app.ActionBar; import android.app.Activity; @@ -30,6 +29,7 @@ import android.content.res.Configuration; import android.content.res.TypedArray; import android.os.Build; import android.os.Bundle; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -61,31 +61,40 @@ import com.android.permissioncontroller.permission.utils.Utils; import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -/** - * The permission details page showing the history/timeline of a permission - */ +/** The permission details page showing the history/timeline of a permission */ @RequiresApi(Build.VERSION_CODES.S) -public class PermissionDetailsFragment extends SettingsWithLargeHeader implements - PermissionUsages.PermissionsUsagesChangeCallback { +public class PermissionDetailsFragment extends SettingsWithLargeHeader + implements PermissionUsages.PermissionsUsagesChangeCallback { public static final int FILTER_7_DAYS = 1; private static final String KEY_SHOW_SYSTEM_PREFS = "_show_system"; - private static final String SHOW_SYSTEM_KEY = PermissionDetailsFragment.class.getName() - + KEY_SHOW_SYSTEM_PREFS; + private static final String SHOW_SYSTEM_KEY = + PermissionDetailsFragment.class.getName() + KEY_SHOW_SYSTEM_PREFS; private static final String KEY_SESSION_ID = "_session_id"; - private static final String SESSION_ID_KEY = PermissionDetailsFragment.class.getName() - + KEY_SESSION_ID; + private static final String SESSION_ID_KEY = + PermissionDetailsFragment.class.getName() + KEY_SESSION_ID; private static final int MENU_SHOW_7_DAYS_DATA = Menu.FIRST + 4; private static final int MENU_SHOW_24_HOURS_DATA = Menu.FIRST + 5; - + private static final long MIDNIGHT_TODAY = + ZonedDateTime.now(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS).toEpochSecond() + * 1000L; + private static final long MIDNIGHT_YESTERDAY = + ZonedDateTime.now(ZoneId.systemDefault()) + .minusDays(1) + .truncatedTo(ChronoUnit.DAYS) + .toEpochSecond() + * 1000L; private @Nullable String mFilterGroup; private int mFilterTimeIndex; private @Nullable List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>(); @@ -117,10 +126,12 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY); mSessionId = savedInstanceState.getLong(SESSION_ID_KEY); } else { - mShowSystem = getArguments().getBoolean( - ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false); - mShow7Days = is7DayToggleEnabled() && getArguments().getBoolean( - ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false); + mShowSystem = + getArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false); + mShow7Days = + KotlinUtils.INSTANCE.is7DayToggleEnabled() + && getArguments() + .getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false); mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); } @@ -139,51 +150,63 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement mPermissionUsages = new PermissionUsages(context); mRoleManager = Utils.getSystemServiceSafe(context, RoleManager.class); - PermissionUsageDetailsViewModelFactory factory = new PermissionUsageDetailsViewModelFactory( - PermissionControllerApplication.get(), mRoleManager, mFilterGroup, mSessionId); - mViewModel = new ViewModelProvider(this, factory).get( - PermissionUsageDetailsViewModel.class); + PermissionUsageDetailsViewModelFactory factory = + new PermissionUsageDetailsViewModelFactory( + PermissionControllerApplication.get(), + mRoleManager, + mFilterGroup, + mSessionId); + mViewModel = + new ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel.class); reloadData(); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - ViewGroup rootView = (ViewGroup) super.onCreateView(inflater, container, - savedInstanceState); + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + ViewGroup rootView = + (ViewGroup) super.onCreateView(inflater, container, savedInstanceState); - PermissionDetailsWrapperFragment parentFragment = (PermissionDetailsWrapperFragment) - requireParentFragment(); + PermissionDetailsWrapperFragment parentFragment = + (PermissionDetailsWrapperFragment) requireParentFragment(); CoordinatorLayout coordinatorLayout = parentFragment.getCoordinatorLayout(); inflater.inflate(R.layout.permission_details_extended_fab, coordinatorLayout); - ExtendedFloatingActionButton extendedFab = coordinatorLayout.requireViewById( - R.id.extended_fab); + ExtendedFloatingActionButton extendedFab = + coordinatorLayout.requireViewById(R.id.extended_fab); // Load the background tint color from the application theme // rather than the Material Design theme Activity activity = getActivity(); - ColorStateList backgroundColor = activity.getColorStateList( - android.R.color.system_accent3_100); + ColorStateList backgroundColor = + activity.getColorStateList(android.R.color.system_accent3_100); extendedFab.setBackgroundTintList(backgroundColor); extendedFab.setText(R.string.manage_permission); - boolean isUiModeNight = (activity.getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - int textColorAttr = isUiModeNight ? android.R.attr.textColorPrimaryInverse - : android.R.attr.textColorPrimary; - TypedArray typedArray = activity.obtainStyledAttributes(new int[] { textColorAttr }); + boolean isUiModeNight = + (activity.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + int textColorAttr = + isUiModeNight + ? android.R.attr.textColorPrimaryInverse + : android.R.attr.textColorPrimary; + TypedArray typedArray = activity.obtainStyledAttributes(new int[] {textColorAttr}); ColorStateList textColor = typedArray.getColorStateList(0); typedArray.recycle(); extendedFab.setTextColor(textColor); extendedFab.setIcon(activity.getDrawable(R.drawable.ic_settings_outline)); extendedFab.setVisibility(View.VISIBLE); - extendedFab.setOnClickListener(view -> { - Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) - .putExtra(Intent.EXTRA_PERMISSION_NAME, mFilterGroup); - startActivity(intent); - }); + extendedFab.setOnClickListener( + view -> { + Intent intent = + new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) + .putExtra(Intent.EXTRA_PERMISSION_NAME, mFilterGroup); + startActivity(intent); + }); RecyclerView recyclerView = getListView(); - int bottomPadding = getResources() - .getDimensionPixelSize(R.dimen.privhub_details_recycler_view_bottom_padding); + int bottomPadding = + getResources() + .getDimensionPixelSize( + R.dimen.privhub_details_recycler_view_bottom_padding); recyclerView.setPadding(0, 0, 0, bottomPadding); recyclerView.setClipToPadding(false); recyclerView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY); @@ -196,8 +219,12 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement super.onStart(); CharSequence title = getString(R.string.permission_history_title); if (mFilterGroup != null) { - title = getResources().getString(R.string.permission_group_usage_title, - KotlinUtils.INSTANCE.getPermGroupLabel(getActivity(), mFilterGroup)); + title = + getResources() + .getString( + R.string.permission_group_usage_title, + KotlinUtils.INSTANCE.getPermGroupLabel( + getActivity(), mFilterGroup)); } getActivity().setTitle(title); } @@ -210,7 +237,7 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages()); // Ensure the group name is valid. - if (mViewModel.getGroup(mFilterGroup, mAppPermissionUsages) == null) { + if (!mViewModel.containsPlatformAppPermissionGroup(mAppPermissionUsages, mFilterGroup)) { mFilterGroup = null; } @@ -226,15 +253,23 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, - R.string.menu_show_system); - mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, - R.string.menu_hide_system); - if (is7DayToggleEnabled()) { - mShow7DaysDataMenu = menu.add(Menu.NONE, MENU_SHOW_7_DAYS_DATA, Menu.NONE, - R.string.menu_show_7_days_data); - mShow24HoursDataMenu = menu.add(Menu.NONE, MENU_SHOW_24_HOURS_DATA, Menu.NONE, - R.string.menu_show_24_hours_data); + mShowSystemMenu = + menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, R.string.menu_show_system); + mHideSystemMenu = + menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system); + if (KotlinUtils.INSTANCE.is7DayToggleEnabled()) { + mShow7DaysDataMenu = + menu.add( + Menu.NONE, + MENU_SHOW_7_DAYS_DATA, + Menu.NONE, + R.string.menu_show_7_days_data); + mShow24HoursDataMenu = + menu.add( + Menu.NONE, + MENU_SHOW_24_HOURS_DATA, + Menu.NONE, + R.string.menu_show_24_hours_data); } updateMenu(); @@ -280,7 +315,8 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement break; case MENU_SHOW_7_DAYS_DATA: case MENU_SHOW_24_HOURS_DATA: - mShow7Days = is7DayToggleEnabled() && itemId == MENU_SHOW_7_DAYS_DATA; + mShow7Days = KotlinUtils.INSTANCE.is7DayToggleEnabled() + && itemId == MENU_SHOW_7_DAYS_DATA; updateUI(); updateMenu(); break; @@ -301,94 +337,125 @@ public class PermissionDetailsFragment extends SettingsWithLargeHeader implement } screen.removeAll(); - Set<String> exemptedPackages = Utils.getExemptedPackages(mRoleManager); - Preference subtitlePreference = new Preference(context); - int usageSubtitle = mShow7Days - ? R.string.permission_group_usage_subtitle_7d - : R.string.permission_group_usage_subtitle_24h; + int usageSubtitle = + mShow7Days + ? R.string.permission_group_usage_subtitle_7d + : R.string.permission_group_usage_subtitle_24h; subtitlePreference.setSummary( - getResources().getString(usageSubtitle, - KotlinUtils.INSTANCE.getPermGroupLabel(getActivity(), mFilterGroup))); + getResources() + .getString( + usageSubtitle, + KotlinUtils.INSTANCE.getPermGroupLabel( + getActivity(), mFilterGroup))); subtitlePreference.setSelectable(false); screen.addPreference(subtitlePreference); - AtomicBoolean seenSystemApp = new AtomicBoolean(false); + PermissionUsageDetailsViewModel.PermissionUsageDetailsUiData uiData = + mViewModel.buildPermissionUsageDetailsUiData( + mAppPermissionUsages, mShowSystem, mShow7Days); - ArrayList<PermissionApps.PermissionApp> permApps = new ArrayList<>(); - List<PermissionUsageDetailsViewModel.AppPermissionUsageEntry> usages = - mViewModel.parseUsages(mAppPermissionUsages, exemptedPackages, permApps, - seenSystemApp, mShowSystem, mShow7Days); + boolean seenSystemApp = uiData.getShouldDisplayShowSystemToggle(); + List<PermissionApps.PermissionApp> permissionApps = uiData.getPermissionApps(); - if (mHasSystemApps != seenSystemApp.get()) { - mHasSystemApps = seenSystemApp.get(); + if (mHasSystemApps != seenSystemApp) { + mHasSystemApps = seenSystemApp; getActivity().invalidateOptionsMenu(); } // Make these variables effectively final so that // we can use these captured variables in the below lambda expression - PreferenceFactory preferenceFactory = new PreferenceFactory(requireActivity()); - AtomicReference<PreferenceCategory> category = new AtomicReference<>( - preferenceFactory.createDayCategoryPreference()); + AtomicReference<PreferenceCategory> category = + new AtomicReference<>(createDayCategoryPreference()); screen.addPreference(category.get()); PreferenceScreen finalScreen = screen; - new PermissionApps.AppDataLoader(context, () -> { - if (getActivity() == null) { - // Fragment has no Activity, return. - return; - } - mViewModel.renderTimelinePreferences(usages, category, finalScreen, preferenceFactory); - - setLoading(false, true); - mFinishedInitialLoad = true; - setProgressBarVisible(false); - mPermissionUsages.stopLoader(getActivity().getLoaderManager()); - - }).execute(permApps.toArray(new PermissionApps.PermissionApp[permApps.size()])); + new PermissionApps.AppDataLoader( + context, + () -> { + if (getActivity() == null) { + // Fragment has no Activity, return. + return; + } + renderHistoryPreferences( + uiData.getHistoryPreferenceDataList(), category, finalScreen); + + setLoading(false, true); + mFinishedInitialLoad = true; + setProgressBarVisible(false); + mPermissionUsages.stopLoader(getActivity().getLoaderManager()); + }) + .execute( + permissionApps.toArray( + new PermissionApps.PermissionApp[permissionApps.size()])); } - private static class PreferenceFactory implements - PermissionUsageDetailsViewModel.HistoryPreferenceFactory { - - private Context mContext; - - PreferenceFactory(Context context) { - mContext = context; - } + /** Render the provided [historyPreferenceDataList] into the [preferenceScreen] UI. */ + private void renderHistoryPreferences( + List<PermissionUsageDetailsViewModel.HistoryPreferenceData> historyPreferenceDataList, + AtomicReference<PreferenceCategory> category, + PreferenceScreen preferenceScreen) { + Context context = getContext(); + long previousDateMs = 0L; + for (int i = 0; i < historyPreferenceDataList.size(); i++) { + PermissionUsageDetailsViewModel.HistoryPreferenceData historyPreferenceData = + historyPreferenceDataList.get(i); + long accessEndTime = historyPreferenceData.getAccessEndTime(); + long currentDateMs = + ZonedDateTime.ofInstant( + Instant.ofEpochMilli(accessEndTime), + Clock.system(ZoneId.systemDefault()).getZone()) + .truncatedTo(ChronoUnit.DAYS) + .toEpochSecond() + * 1000L; + if (currentDateMs != previousDateMs) { + if (previousDateMs != 0L) { + category.set(createDayCategoryPreference()); + preferenceScreen.addPreference(category.get()); + } + if (accessEndTime > MIDNIGHT_TODAY) { + category.get().setTitle(R.string.permission_history_category_today); + } else if (accessEndTime > MIDNIGHT_YESTERDAY) { + category.get().setTitle(R.string.permission_history_category_yesterday); + } else { + category.get() + .setTitle(DateFormat.getDateFormat(context).format(currentDateMs)); + } + previousDateMs = currentDateMs; + } - @Override - public PreferenceCategory createDayCategoryPreference() { - PreferenceCategory category = new PreferenceCategory(mContext); - // Do not reserve icon space, so that the text moves all the way left. - category.setIconSpaceReserved(false); - return category; + Preference permissionUsagePreference = + new PermissionHistoryPreference( + getContext(), + historyPreferenceData.getUserHandle(), + historyPreferenceData.getPkgName(), + historyPreferenceData.getAppIcon(), + historyPreferenceData.getPreferenceTitle(), + historyPreferenceData.getPermissionGroup(), + DateFormat.getTimeFormat(getContext()) + .format(historyPreferenceData.getAccessEndTime()), + historyPreferenceData.getSummaryText(), + historyPreferenceData.getShowingAttribution(), + historyPreferenceData.getAccessTimeList(), + historyPreferenceData.getAttributionTags(), + i == historyPreferenceDataList.size() - 1, + historyPreferenceData.getSessionId()); + + category.get().addPreference(permissionUsagePreference); } + } - @Override - public Preference createPermissionHistoryPreference( - PermissionUsageDetailsViewModel.HistoryPreferenceData historyPreferenceData) { - return new PermissionHistoryPreference(mContext, - historyPreferenceData.getUserHandle(), - historyPreferenceData.getPkgName(), - historyPreferenceData.getAppIcon(), - historyPreferenceData.getPreferenceTitle(), - historyPreferenceData.getPermissionGroup(), - historyPreferenceData.getAccessTime(), - historyPreferenceData.getSummaryText(), - historyPreferenceData.getShowingAttribution(), - historyPreferenceData.getAccessTimeList(), - historyPreferenceData.getAttributionTags(), - historyPreferenceData.isLastUsage(), - historyPreferenceData.getSessionId() - ); - } + private PreferenceCategory createDayCategoryPreference() { + PreferenceCategory category = new PreferenceCategory(getContext()); + // Do not reserve icon space, so that the text moves all the way left. + category.setIconSpaceReserved(false); + return category; } private void reloadData() { - mViewModel.loadPermissionUsages(getActivity().getLoaderManager(), - mPermissionUsages, this, mFilterTimeIndex); + mViewModel.loadPermissionUsages( + getActivity().getLoaderManager(), mPermissionUsages, this, mFilterTimeIndex); if (mFinishedInitialLoad) { setProgressBarVisible(true); } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java index 46f016455..39cf050ac 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java @@ -22,11 +22,9 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.PERM import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED; import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED; import static com.android.permissioncontroller.PermissionControllerStatsLog.write; -import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled; +import android.Manifest; import android.app.ActionBar; -import android.app.Activity; -import android.app.role.RoleManager; import android.content.Context; import android.os.Build; import android.os.Bundle; @@ -34,8 +32,6 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; @@ -45,61 +41,52 @@ import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.permissioncontroller.R; -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage; -import com.android.permissioncontroller.permission.model.v31.PermissionUsages; -import com.android.permissioncontroller.permission.model.legacy.PermissionApps; import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader; -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel; -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelFactory; +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelNew; import com.android.permissioncontroller.permission.utils.KotlinUtils; -import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.HelpUtils; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; -import kotlin.Triple; - -/** - * The main page for the privacy dashboard. - */ +/** The main page for the privacy dashboard. */ +// TODO(b/257317510): Remove "V2" suffix. @RequiresApi(Build.VERSION_CODES.S) -public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implements - PermissionUsages.PermissionsUsagesChangeCallback { +public class PermissionUsageV2Fragment extends SettingsWithLargeHeader { + + private static final Map<String, Integer> PERMISSION_GROUP_ORDER = + Map.of( + Manifest.permission_group.LOCATION, 0, + Manifest.permission_group.CAMERA, 1, + Manifest.permission_group.MICROPHONE, 2); + private static final int DEFAULT_ORDER = 3; // Pie chart in this screen will be the first child. // Hence we use PERMISSION_GROUP_ORDER + 1 here. private static final int PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT = - PermissionUsageViewModel.Companion.getPERMISSION_GROUP_ORDER().size() + 1; + PERMISSION_GROUP_ORDER.size() + 1; private static final int EXPAND_BUTTON_ORDER = 999; - + /** Map to represent ordering for permission groups in the permissions usage UI. */ private static final String KEY_SESSION_ID = "_session_id"; - private static final String SESSION_ID_KEY = PermissionUsageV2Fragment.class.getName() - + KEY_SESSION_ID; + + private static final String SESSION_ID_KEY = + PermissionUsageV2Fragment.class.getName() + KEY_SESSION_ID; private static final int MENU_SHOW_7_DAYS_DATA = Menu.FIRST + 4; private static final int MENU_SHOW_24_HOURS_DATA = Menu.FIRST + 5; private static final int MENU_REFRESH = Menu.FIRST + 6; - private @NonNull PermissionUsages mPermissionUsages; - private @Nullable List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>(); - - private PermissionUsageViewModel mViewModel; + private PermissionUsageViewModelNew mViewModel; - private boolean mShowSystem; private boolean mHasSystemApps; private MenuItem mShowSystemMenu; private MenuItem mHideSystemMenu; - private boolean mShow7Days; private MenuItem mShow7DaysDataMenu; private MenuItem mShow24HoursDataMenu; private boolean mOtherExpanded; - private boolean mFinishedInitialLoad; - - private @NonNull RoleManager mRoleManager; - private PermissionUsageGraphicPreference mGraphic; /** Unique Id of a request */ @@ -115,13 +102,16 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); } - mFinishedInitialLoad = false; + PermissionUsageViewModelNew.PermissionUsageViewModelFactory factory = + new PermissionUsageViewModelNew.PermissionUsageViewModelFactory( + getActivity().getApplication(), this, new Bundle()); + mViewModel = new ViewModelProvider(this, factory).get(PermissionUsageViewModelNew.class); // By default, do not show system app usages. - mShowSystem = false; + mViewModel.updateShowSystem(false); // By default, show permission usages for the past 24 hours. - mShow7Days = false; + mViewModel.updateShow7Days(false); // Start out with 'other' permissions not expanded. mOtherExpanded = false; @@ -133,14 +123,9 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement ab.setDisplayHomeAsUpEnabled(true); } - Context context = getPreferenceManager().getContext(); - mPermissionUsages = new PermissionUsages(context); - mRoleManager = Utils.getSystemServiceSafe(context, RoleManager.class); - - PermissionUsageViewModelFactory factory = new PermissionUsageViewModelFactory(mRoleManager); - mViewModel = new ViewModelProvider(this, factory).get(PermissionUsageViewModel.class); - - reloadData(); + mViewModel.getPermissionUsagesUiLiveData().observe(this, this::updateUI); + mViewModel.getShowSystemLiveData().observe(this, this::updateShowSystem); + mViewModel.getShow7DaysLiveData().observe(this, this::updateShow7Days); } @Override @@ -148,39 +133,40 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement PreferenceGroupAdapter adapter = (PreferenceGroupAdapter) super.onCreateAdapter(preferenceScreen); - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - updatePreferenceScreenAdvancedTitleAndSummary(preferenceScreen, adapter); - } - - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - onChanged(); - } - - @Override - public void onItemRangeRemoved(int positionStart, int itemCount) { - onChanged(); - } - - @Override - public void onItemRangeChanged(int positionStart, int itemCount) { - onChanged(); - } - - @Override - public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { - onChanged(); - } - }); + adapter.registerAdapterDataObserver( + new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + updatePreferenceScreenAdvancedTitleAndSummary(preferenceScreen, adapter); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + onChanged(); + } + }); updatePreferenceScreenAdvancedTitleAndSummary(preferenceScreen, adapter); return adapter; } - private void updatePreferenceScreenAdvancedTitleAndSummary(PreferenceScreen preferenceScreen, - PreferenceGroupAdapter adapter) { + private void updatePreferenceScreenAdvancedTitleAndSummary( + PreferenceScreen preferenceScreen, PreferenceGroupAdapter adapter) { int count = adapter.getItemCount(); if (count == 0) { return; @@ -215,26 +201,33 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); if (mHasSystemApps) { - mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, - R.string.menu_show_system); - mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, - R.string.menu_hide_system); + mShowSystemMenu = + menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, R.string.menu_show_system); + mHideSystemMenu = + menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system); } - if (is7DayToggleEnabled()) { - mShow7DaysDataMenu = menu.add(Menu.NONE, MENU_SHOW_7_DAYS_DATA, Menu.NONE, - R.string.menu_show_7_days_data); - mShow24HoursDataMenu = menu.add(Menu.NONE, MENU_SHOW_24_HOURS_DATA, Menu.NONE, - R.string.menu_show_24_hours_data); + if (KotlinUtils.INSTANCE.is7DayToggleEnabled()) { + mShow7DaysDataMenu = + menu.add( + Menu.NONE, + MENU_SHOW_7_DAYS_DATA, + Menu.NONE, + R.string.menu_show_7_days_data); + mShow24HoursDataMenu = + menu.add( + Menu.NONE, + MENU_SHOW_24_HOURS_DATA, + Menu.NONE, + R.string.menu_show_24_hours_data); } - HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_permission_usage, - getClass().getName()); - MenuItem refresh = menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, - R.string.permission_usage_refresh); + HelpUtils.prepareHelpMenuItem( + getActivity(), menu, R.string.help_permission_usage, getClass().getName()); + MenuItem refresh = + menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.permission_usage_refresh); refresh.setIcon(R.drawable.ic_refresh); refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - updateMenu(); } @Override @@ -245,52 +238,28 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement getActivity().finishAfterTransition(); return true; case MENU_SHOW_SYSTEM: - write(PERMISSION_USAGE_FRAGMENT_INTERACTION, mSessionId, + write( + PERMISSION_USAGE_FRAGMENT_INTERACTION, + mSessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED); - // fall through + mViewModel.updateShowSystem(true); + break; case MENU_HIDE_SYSTEM: - mShowSystem = itemId == MENU_SHOW_SYSTEM; - // We already loaded all data, so don't reload - updateUI(); - updateMenu(); + mViewModel.updateShowSystem(false); break; case MENU_SHOW_7_DAYS_DATA: + mViewModel.updateShow7Days(KotlinUtils.INSTANCE.is7DayToggleEnabled()); + break; case MENU_SHOW_24_HOURS_DATA: - mShow7Days = is7DayToggleEnabled() && itemId == MENU_SHOW_7_DAYS_DATA; - updateUI(); - updateMenu(); + mViewModel.updateShow7Days(false); break; case MENU_REFRESH: - reloadData(); + // TODO(b/257314894): What should happen on refresh? break; } return super.onOptionsItemSelected(item); } - private void updateMenu() { - if (mHasSystemApps) { - mShowSystemMenu.setVisible(!mShowSystem); - mHideSystemMenu.setVisible(mShowSystem); - } - - if (mShow7DaysDataMenu != null) { - mShow7DaysDataMenu.setVisible(!mShow7Days); - } - - if (mShow24HoursDataMenu != null) { - mShow24HoursDataMenu.setVisible(mShow7Days); - } - } - - @Override - public void onPermissionUsagesChanged() { - if (mPermissionUsages.getUsages().isEmpty()) { - return; - } - mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages()); - updateUI(); - } - @Override public int getEmptyViewString() { return R.string.no_permission_usages; @@ -304,8 +273,26 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement } } - private void updateUI() { - if (mAppPermissionUsages.isEmpty() || getActivity() == null) { + private void updateShowSystem(boolean showSystem) { + if (mHasSystemApps) { + mShowSystemMenu.setVisible(!showSystem); + mHideSystemMenu.setVisible(showSystem); + } + } + + private void updateShow7Days(boolean show7Days) { + if (mShow7DaysDataMenu != null) { + mShow7DaysDataMenu.setVisible(!show7Days); + } + + if (mShow24HoursDataMenu != null) { + mShow24HoursDataMenu.setVisible(show7Days); + } + } + + private void updateUI( + PermissionUsageViewModelNew.PermissionUsagesUiData permissionUsagesUiData) { + if (getActivity() == null) { return; } Context context = getActivity(); @@ -324,106 +311,127 @@ public class PermissionUsageV2Fragment extends SettingsWithLargeHeader implement PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT); } screen.setOnExpandButtonClickListener(() -> { - write(PERMISSION_USAGE_FRAGMENT_INTERACTION, mSessionId, + write( + PERMISSION_USAGE_FRAGMENT_INTERACTION, + mSessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED); }); - - Triple<Map<String, Integer>, ArrayList<PermissionApps.PermissionApp>, Boolean> - triple = mViewModel.extractUsages(mAppPermissionUsages, mShow7Days, mShowSystem); - Map<String, Integer> usages = triple.getFirst(); - ArrayList<PermissionApps.PermissionApp> permApps = triple.getSecond(); - boolean seenSystemApp = triple.getThird(); - - if (mHasSystemApps != seenSystemApp) { - mHasSystemApps = seenSystemApp; + boolean displayShowSystemToggle = permissionUsagesUiData.getDisplayShowSystemToggle(); + Map<String, Integer> permissionGroupWithUsageCounts = + permissionUsagesUiData.getPermissionGroupsWithUsageCount(); + List<Map.Entry<String, Integer>> permissionGroupWithUsageCountsEntries = + new ArrayList(permissionGroupWithUsageCounts.entrySet()); + + permissionGroupWithUsageCountsEntries.sort(Comparator.comparing( + (Map.Entry<String, Integer> permissionGroupWithUsageCount) -> + PERMISSION_GROUP_ORDER.getOrDefault( + permissionGroupWithUsageCount.getKey(), + DEFAULT_ORDER)) + .thenComparing( + (Map.Entry<String, Integer> permissionGroupWithUsageCount) -> + KotlinUtils.INSTANCE + .getPermGroupLabel( + context, + permissionGroupWithUsageCount + .getKey()) + .toString())); + + if (mHasSystemApps != displayShowSystemToggle) { + mHasSystemApps = displayShowSystemToggle; getActivity().invalidateOptionsMenu(); } - mGraphic = new PermissionUsageGraphicPreference(context, mShow7Days); + mGraphic = + new PermissionUsageGraphicPreference( + context, permissionUsagesUiData.getShow7DaysUsage()); screen.addPreference(mGraphic); - mGraphic.setUsages(usages); + + mGraphic.setUsages(permissionGroupWithUsageCounts); // Add the preference header. PreferenceCategory category = new PreferenceCategory(context); screen.addPreference(category); - List<Map.Entry<String, Integer>> groupUsagesList = mViewModel.createGroupUsagesList( - getContext(), usages); - - CharSequence advancedInfoSummary = getAdvancedInfoSummaryString(context, groupUsagesList); + CharSequence advancedInfoSummary = + getAdvancedInfoSummaryString(context, permissionGroupWithUsageCountsEntries); screen.setSummary(advancedInfoSummary); - addUIContent(context, groupUsagesList, permApps, category); + addUIContent( + context, + permissionGroupWithUsageCountsEntries, + category, + permissionUsagesUiData.getShowSystemAppPermissions(), + permissionUsagesUiData.getShow7DaysUsage()); } - private CharSequence getAdvancedInfoSummaryString(Context context, - List<Map.Entry<String, Integer>> groupUsagesList) { - int size = groupUsagesList.size(); + private CharSequence getAdvancedInfoSummaryString( + Context context, List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts) { + int size = permissionGroupWithUsageCounts.size(); if (size <= PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1) { return ""; } // case for 1 extra item in the advanced info if (size == PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT) { - String permGroupName = groupUsagesList - .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1).getKey(); + String permGroupName = + permissionGroupWithUsageCounts + .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1) + .getKey(); return KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName); } - String permGroupName1 = groupUsagesList - .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1).getKey(); - String permGroupName2 = groupUsagesList - .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT).getKey(); - CharSequence permGroupLabel1 = KotlinUtils - .INSTANCE.getPermGroupLabel(context, permGroupName1); - CharSequence permGroupLabel2 = KotlinUtils - .INSTANCE.getPermGroupLabel(context, permGroupName2); + String permGroupName1 = + permissionGroupWithUsageCounts + .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1) + .getKey(); + String permGroupName2 = + permissionGroupWithUsageCounts + .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT) + .getKey(); + CharSequence permGroupLabel1 = + KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName1); + CharSequence permGroupLabel2 = + KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName2); // case for 2 extra items in the advanced info if (size == PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT + 1) { - return context.getResources().getString(R.string.perm_usage_adv_info_summary_2_items, - permGroupLabel1, permGroupLabel2); + return context.getResources() + .getString( + R.string.perm_usage_adv_info_summary_2_items, + permGroupLabel1, + permGroupLabel2); } // case for 3 or more extra items in the advanced info int numExtraItems = size - PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1; - return context.getResources().getString(R.string.perm_usage_adv_info_summary_more_items, - permGroupLabel1, permGroupLabel2, numExtraItems); + return context.getResources() + .getString( + R.string.perm_usage_adv_info_summary_more_items, + permGroupLabel1, + permGroupLabel2, + numExtraItems); } - /** - * Use the usages and permApps that are previously constructed to add UI content to the page - */ - private void addUIContent(Context context, - List<Map.Entry<String, Integer>> usages, - ArrayList<PermissionApps.PermissionApp> permApps, - PreferenceCategory category) { - new PermissionApps.AppDataLoader(context, () -> { - for (int i = 0; i < usages.size(); i++) { - Map.Entry<String, Integer> currentEntry = usages.get(i); - PermissionUsageV2ControlPreference permissionUsagePreference = - new PermissionUsageV2ControlPreference(context, currentEntry.getKey(), - currentEntry.getValue(), mShowSystem, mSessionId, mShow7Days); - category.addPreference(permissionUsagePreference); - } - - setLoading(false, true); - mFinishedInitialLoad = true; - setProgressBarVisible(false); - - Activity activity = getActivity(); - if (activity != null) { - mPermissionUsages.stopLoader(activity.getLoaderManager()); - } - }).execute(permApps.toArray(new PermissionApps.PermissionApp[0])); - } - - /** - * Reloads the data to show. - */ - private void reloadData() { - mViewModel.loadPermissionUsages(getActivity().getLoaderManager(), mPermissionUsages, this); - if (mFinishedInitialLoad) { - setProgressBarVisible(true); + /** Use the usages and permApps that are previously constructed to add UI content to the page */ + private void addUIContent( + Context context, + List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts, + PreferenceCategory category, + boolean showSystem, + boolean show7Days) { + for (int i = 0; i < permissionGroupWithUsageCounts.size(); i++) { + Map.Entry<String, Integer> permissionGroupWithUsageCount = + permissionGroupWithUsageCounts.get(i); + PermissionUsageV2ControlPreference permissionUsagePreference = + new PermissionUsageV2ControlPreference( + context, + permissionGroupWithUsageCount.getKey(), + permissionGroupWithUsageCount.getValue(), + showSystem, + mSessionId, + show7Days); + category.addPreference(permissionUsagePreference); } + + setLoading(false, true); } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt index bb060e831..5e3cd8684 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt @@ -38,7 +38,6 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISS import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED import com.android.permissioncontroller.R import com.android.permissioncontroller.hibernation.isHibernationEnabled -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData @@ -47,19 +46,20 @@ import com.android.permissioncontroller.permission.data.PackagePermissionsLiveDa import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category -import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled import com.android.permissioncontroller.permission.utils.IPC +import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType import com.android.permissioncontroller.permission.utils.navigateSafe -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import java.time.Instant import java.util.concurrent.TimeUnit import kotlin.math.max +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch /** * ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all @@ -281,7 +281,7 @@ class AppPermissionGroupsViewModel( return } - val aggregateDataFilterBeginDays = if (is7DayToggleEnabled()) + val aggregateDataFilterBeginDays = if (KotlinUtils.is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 accessTime.clear() diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt index 98bb5d80c..0a56095e5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt @@ -19,21 +19,27 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION -import android.Manifest.permission_group.LOCATION +import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED +import android.Manifest.permission_group.READ_MEDIA_VISUAL import android.annotation.SuppressLint import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE import android.app.Application +import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle import android.os.UserHandle +import android.provider.MediaStore import android.util.Log +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContract import androidx.annotation.ChecksSdkIntAtLeast import androidx.annotation.RequiresApi import androidx.annotation.StringRes +import androidx.core.util.Consumer import androidx.fragment.app.Fragment import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -44,7 +50,6 @@ import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_VIEWED import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData @@ -55,9 +60,6 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPerm import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs - -import com.android.permissioncontroller.permission.ui.handheld.v31.getDefaultPrecision -import com.android.permissioncontroller.permission.ui.handheld.v31.isLocationAccuracyEnabled import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_ALWAYS import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND @@ -66,8 +68,12 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY_FOREGROUND import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.LOCATION_ACCURACY +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.SELECT_PHOTOS import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision +import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled import com.android.permissioncontroller.permission.utils.LocationUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.SafetyNetLogger import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.navigateSafe @@ -98,6 +104,7 @@ class AppPermissionViewModel( private val LOG_TAG = AppPermissionViewModel::class.java.simpleName private const val DEVICE_PROFILE_ROLE_PREFIX = "android.app.role" + const val PHOTO_PICKER_REQUEST_CODE = 1 } interface ConfirmDialogShowingFragment { @@ -112,21 +119,22 @@ class AppPermissionViewModel( } enum class ChangeRequest(val value: Int) { - GRANT_FOREGROUND(1), - REVOKE_FOREGROUND(2), - GRANT_BACKGROUND(4), - REVOKE_BACKGROUND(8), + GRANT_FOREGROUND(1 shl 0), + REVOKE_FOREGROUND(1 shl 1), + GRANT_BACKGROUND(1 shl 2), + REVOKE_BACKGROUND(1 shl 3), GRANT_BOTH(GRANT_FOREGROUND.value or GRANT_BACKGROUND.value), REVOKE_BOTH(REVOKE_FOREGROUND.value or REVOKE_BACKGROUND.value), GRANT_FOREGROUND_ONLY(GRANT_FOREGROUND.value or REVOKE_BACKGROUND.value), - GRANT_All_FILE_ACCESS(16), - GRANT_FINE_LOCATION(32), - REVOKE_FINE_LOCATION(64), - GRANT_STORAGE_SUPERGROUP(128), - REVOKE_STORAGE_SUPERGROUP(256), + GRANT_All_FILE_ACCESS(1 shl 4), + GRANT_FINE_LOCATION(1 shl 5), + REVOKE_FINE_LOCATION(1 shl 6), + GRANT_STORAGE_SUPERGROUP(1 shl 7), + REVOKE_STORAGE_SUPERGROUP(1 shl 8), GRANT_STORAGE_SUPERGROUP_CONFIRMED( GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value), - REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value); + REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value), + PHOTOS_SELECTED( 1 shl 9); infix fun andValue(other: ChangeRequest): Int { return value and other.value @@ -141,13 +149,16 @@ class AppPermissionViewModel( ASK(4), DENY(5), DENY_FOREGROUND(6), - LOCATION_ACCURACY(7); + LOCATION_ACCURACY(7), + SELECT_PHOTOS( 8); } private val isStorageAndLessThanT = permGroupName == Manifest.permission_group.STORAGE && !SdkLevel.isAtLeastT() private var hasConfirmedRevoke = false private var lightAppPermGroup: LightAppPermGroup? = null + private var photoPickerLauncher: ActivityResultLauncher<Unit>? = null + private var photoPickerResultConsumer: Consumer<Int>? = null private val mediaStorageSupergroupPermGroups = mutableMapOf<String, LightAppPermGroup>() @@ -200,8 +211,8 @@ class AppPermissionViewModel( /** * A livedata which computes the state of the radio buttons */ - val buttonStateLiveData = object - : SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() { + val buttonStateLiveData = object : + SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() { private val appPermGroupLiveData = LightAppPermGroupLiveData[packageName, permGroupName, user] @@ -275,6 +286,7 @@ class AppPermissionViewModel( val askState = ButtonState() val deniedState = ButtonState() val deniedForegroundState = ButtonState() + val selectState = ButtonState() askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime askState.isShown = PermissionMapping.supportsOneTimeGrant(permGroupName) && @@ -315,6 +327,19 @@ class AppPermissionViewModel( val detailId = getIndividualPermissionDetailResId(group) detailResIdLiveData.value = detailId.first to detailId.second } + } else if (KotlinUtils.isPhotoPickerPromptEnabled() && + group.permGroupName == READ_MEDIA_VISUAL && + group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) { + // Allow / Select Photos / Deny case + allowedState.isShown = true + deniedState.isShown = true + selectState.isShown = true + + deniedState.isChecked = !group.isGranted + val onlyUserSelectedGranted = group.isGranted && group.permissions.values.all { + it.name == READ_MEDIA_VISUAL_USER_SELECTED || !it.isGrantedIncludingAppOp } + selectState.isChecked = onlyUserSelectedGranted + allowedState.isChecked = group.isGranted && !onlyUserSelectedGranted } else { // Allow / Deny case allowedState.isShown = true @@ -376,9 +401,8 @@ class AppPermissionViewModel( } if (shouldShowLocationAccuracy == null) { - shouldShowLocationAccuracy = group.permGroupName == LOCATION && - group.permissions.containsKey(ACCESS_FINE_LOCATION) && - isLocationAccuracyEnabled() + shouldShowLocationAccuracy = isLocationAccuracyEnabled() && + group.permissions.containsKey(ACCESS_FINE_LOCATION) } val locationAccuracyState = ButtonState(isFineLocationChecked(group), true, false, null) @@ -393,10 +417,36 @@ class AppPermissionViewModel( ALLOW to allowedState, ALLOW_ALWAYS to allowedAlwaysState, ALLOW_FOREGROUND to allowedForegroundState, ASK_ONCE to askOneTimeState, ASK to askState, DENY to deniedState, DENY_FOREGROUND to deniedForegroundState, - LOCATION_ACCURACY to locationAccuracyState) + LOCATION_ACCURACY to locationAccuracyState, SELECT_PHOTOS to selectState) } } + fun registerPhotoPickerResultIfNeeded(fragment: Fragment) { + if (permGroupName != READ_MEDIA_VISUAL) { + return + } + photoPickerLauncher = fragment.registerForActivityResult( + object : ActivityResultContract<Unit, Int>() { + override fun parseResult(resultCode: Int, intent: Intent?): Int { + return resultCode + } + + override fun createIntent(context: Context, input: Unit): Intent { + return Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) + .putExtra(Intent.EXTRA_UID, lightAppPermGroup?.packageInfo?.uid) + .setType(KotlinUtils.getMimeTypeForPermissions( + lightAppPermGroup?.foregroundPermNames ?: emptyList())) + } + }) { result -> + photoPickerResultConsumer?.accept(result) + } + } + + fun openPhotoPicker(onResult: Consumer<Int>) { + photoPickerResultConsumer = onResult + photoPickerLauncher?.launch(Unit) + } + private fun isFineLocationChecked(group: LightAppPermGroup): Boolean { if (shouldShowLocationAccuracy == true) { val coarseLocation = group.permissions[ACCESS_COARSE_LOCATION]!! @@ -551,7 +601,8 @@ class AppPermissionViewModel( if (changeRequest == ChangeRequest.GRANT_FINE_LOCATION) { if (!group.isOneTime) { - KotlinUtils.grantForegroundRuntimePermissions(app, group) + val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, group) + logPermissionChanges(group, newGroup, buttonClicked) } KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, group, true) return @@ -559,13 +610,25 @@ class AppPermissionViewModel( if (changeRequest == ChangeRequest.REVOKE_FINE_LOCATION) { if (!group.isOneTime) { - KotlinUtils.revokeForegroundRuntimePermissions(app, group, + val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, filterPermissions = listOf(ACCESS_FINE_LOCATION)) + logPermissionChanges(group, newGroup, buttonClicked) } KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, group, false) return } + if (changeRequest == ChangeRequest.PHOTOS_SELECTED) { + val nonSelectedPerms = group.permissions.keys.filter { + it != READ_MEDIA_VISUAL_USER_SELECTED } + var newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, + filterPermissions = nonSelectedPerms) + newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup, + filterPermissions = listOf(READ_MEDIA_VISUAL_USER_SELECTED)) + logPermissionChanges(group, newGroup, buttonClicked) + return + } + val shouldGrantForeground = changeRequest andValue ChangeRequest.GRANT_FOREGROUND != 0 val shouldGrantBackground = changeRequest andValue ChangeRequest.GRANT_BACKGROUND != 0 val shouldRevokeForeground = changeRequest andValue ChangeRequest.REVOKE_FOREGROUND != 0 @@ -667,11 +730,12 @@ class AppPermissionViewModel( } if (shouldGrantForeground) { - if (shouldShowLocationAccuracy == true && !isFineLocationChecked(newGroup)) { - newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup, - filterPermissions = listOf(ACCESS_COARSE_LOCATION)) + newGroup = if (shouldShowLocationAccuracy == true && + !isFineLocationChecked(newGroup)) { + KotlinUtils.grantForegroundRuntimePermissions(app, newGroup, + filterPermissions = listOf(ACCESS_COARSE_LOCATION)) } else { - newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup) + KotlinUtils.grantForegroundRuntimePermissions(app, newGroup) } if (!wasForegroundGranted) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt index 5763d92a7..c6c5cef8e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.model.v31 +package com.android.permissioncontroller.permission.ui.model import android.Manifest import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.POST_NOTIFICATIONS +import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED import android.Manifest.permission_group.LOCATION +import android.Manifest.permission_group.READ_MEDIA_VISUAL import android.annotation.SuppressLint import android.app.Activity import android.app.Application @@ -30,15 +32,24 @@ import android.content.pm.PackageManager import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET +import android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS +import android.healthconnect.HealthConnectManager.isHealthPermission +import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Build import android.os.Bundle import android.os.Process import android.permission.PermissionManager +import android.provider.MediaStore import android.util.Log +import androidx.annotation.ChecksSdkIntAtLeast import androidx.core.util.Consumer import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.modules.utils.build.SdkLevel +import com.android.permission.safetylabel.DataCategory +import com.android.permission.safetylabel.DataType +import com.android.permission.safetylabel.DataTypeConstants +import com.android.permission.safetylabel.SafetyLabel import com.android.permissioncontroller.Constants import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.PermissionControllerApplication @@ -50,6 +61,7 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED +import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE @@ -59,10 +71,10 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED import com.android.permissioncontroller.auto.DrivingDecisionReminderService -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData +import com.android.permissioncontroller.permission.data.SafetyLabelLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup @@ -72,16 +84,21 @@ import com.android.permissioncontroller.permission.service.PermissionChangeStora import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_MORE_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON +import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.INTENT_PHOTOS_SELECTED import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_SETTINGS import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON @@ -90,18 +107,26 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.N import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON -import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE_PHOTOS import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT -import com.android.permissioncontroller.permission.ui.handheld.v31.getDefaultPrecision -import com.android.permissioncontroller.permission.ui.handheld.v31.isLocationAccuracyEnabled import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision +import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions +import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions +import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled +import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissionRationaleEnabled +import com.android.permissioncontroller.permission.utils.PermissionMapping +import com.android.permissioncontroller.permission.utils.SafetyLabelPermissionMapping import com.android.permissioncontroller.permission.utils.SafetyNetLogger import com.android.permissioncontroller.permission.utils.Utils @@ -126,6 +151,7 @@ class GrantPermissionsViewModel( private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName private val user = Process.myUserHandle() private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] + private val safetyLabelLiveData = SafetyLabelLiveData[packageName] private val dpm = app.getSystemService(DevicePolicyManager::class.java)!! private val permissionPolicy = dpm.getPermissionPolicy(null) private val permGroupsToSkip = mutableListOf<String>() @@ -138,6 +164,7 @@ class GrantPermissionsViewModel( } private lateinit var packageInfo: LightPackageInfo + private var safetyLabel: SafetyLabel? = null // All permissions that could possibly be affected by the provided requested permissions, before // filtering system fixed, auto grant, etc. @@ -157,7 +184,9 @@ class GrantPermissionsViewModel( val locationVisibilities: List<Boolean> = List(NEXT_LOCATION_DIALOG) { false }, val message: RequestMessage = RequestMessage.FG_MESSAGE, val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE, - val sendToSettingsImmediately: Boolean = false + val sendToSettingsImmediately: Boolean = false, + val openPhotoPicker: Boolean = false, + val showPermissionRationale: Boolean = false ) { val groupName = groupInfo.name } @@ -175,12 +204,16 @@ class GrantPermissionsViewModel( init { addSource(packagePermissionsLiveData) { onPackageLoaded() } addSource(packageInfoLiveData) { onPackageLoaded() } + addSource(safetyLabelLiveData) { onPackageLoaded() } + // Load package state, if available onPackageLoaded() } private fun onPackageLoaded() { - if (packageInfoLiveData.isStale || packagePermissionsLiveData.isStale) { + if (packageInfoLiveData.isStale || + packagePermissionsLiveData.isStale || + safetyLabelLiveData.isStale) { return } @@ -201,13 +234,15 @@ class GrantPermissionsViewModel( return } + safetyLabel = safetyLabelLiveData.value + val allAffectedPermissions = requestedPermissions.toMutableSet() for (requestedPerm in requestedPermissions) { allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups)) } unfilteredAffectedPermissions = allAffectedPermissions.toList() - getAppPermGroups(groups.toMutableMap().apply { + setAppPermGroupsLiveDatas(groups.toMutableMap().apply { remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) }) @@ -217,7 +252,7 @@ class GrantPermissionsViewModel( } } - private fun getAppPermGroups(groups: Map<String, List<String>>) { + private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) { val requestedGroups = groups.filter { (_, perms) -> perms.any { it in unfilteredAffectedPermissions } @@ -274,10 +309,10 @@ class GrantPermissionsViewModel( groupStates = getRequiredGroupStates( appPermGroupLiveDatas.mapNotNull { it.value.value }) } - getRequestInfosFromGroupStates() + setRequestInfosFromGroupStates() } - private fun getRequestInfosFromGroupStates() { + private fun setRequestInfosFromGroupStates() { val requestInfos = mutableListOf<RequestInfo>() for ((key, groupState) in groupStates) { val groupInfo = groupState.group.permGroupInfo @@ -323,7 +358,29 @@ class GrantPermissionsViewModel( // Whether or not to use the foreground, background, or no detail message. // null == var detailMessage = RequestMessage.NO_MESSAGE - if (groupState.group.packageInfo.targetSdkVersion >= + + if (shouldShowMorePhotosMessage(groupState.group)) { + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[DENY_BUTTON] = false + buttonVisibilities[ALLOW_MORE_SELECTED_PHOTOS_BUTTON] = true + buttonVisibilities[DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON] = true + message = RequestMessage.MORE_PHOTOS_MESSAGE + } else if (KotlinUtils.isPhotoPickerPromptEnabled() && + groupState.group.permGroupName == READ_MEDIA_VISUAL && + groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) { + // If the USER_SELECTED permission is user fixed and granted, or the app is only + // requesting USER_SELECTED, direct straight to photo picker + val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] + if ((userPerm?.isUserFixed == true && userPerm.isGrantedIncludingAppOp) || + groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) { + requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true)) + continue + } else { + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[ALLOW_SELECTED_PHOTOS_BUTTON] = true + buttonVisibilities[ALLOW_ALL_PHOTOS_BUTTON] = true + } + } else if (groupState.group.packageInfo.targetSdkVersion >= minSdkForOrderedSplitPermissions) { if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) { if (needFgPermissions) { @@ -440,7 +497,7 @@ class GrantPermissionsViewModel( // Show location permission dialogs based on location permissions val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false } if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() && - packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) { + packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) { if (needFgPermissions) { locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true if (fgState != null && @@ -514,19 +571,12 @@ class GrantPermissionsViewModel( buttonVisibilities, locationVisibilities, message, - detailMessage)) + detailMessage, + showPermissionRationale = shouldShowPermissionRationale( + safetyLabel, groupState))) } - requestInfos.sortWith(Comparator { rhs, lhs -> - val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] - val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] - if (rhsHasOneTime && !lhsHasOneTime) { - -1 - } else if (!rhsHasOneTime && lhsHasOneTime) { - 1 - } else { - rhs.groupName.compareTo(lhs.groupName) - } - }) + + sortPermissionGroups(requestInfos) value = if (requestInfos.any { it.sendToSettingsImmediately } && requestInfos.size > 1) { @@ -539,6 +589,57 @@ class GrantPermissionsViewModel( } } + fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) { + requestInfos.sortWith { rhs, lhs -> + val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] + val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] + if (rhsHasOneTime && !lhsHasOneTime) { + -1 + } else if ((!rhsHasOneTime && lhsHasOneTime) || + isHealthPermissionGroup(rhs.groupName) + ) { + 1 + } else { + rhs.groupName.compareTo(lhs.groupName) + } + } + } + + private fun shouldShowPermissionRationale( + safetyLabel: SafetyLabel?, + groupState: GroupState + ): Boolean { + if (!isPermissionRationaleEnabled() || + safetyLabel == null || + safetyLabel.dataLabel.dataShared.isEmpty()) { + return false + } + + val groupName = groupState.group.permGroupName + val categoriesForPermission: List<String> = + SafetyLabelPermissionMapping.getCategoriesForPermissionGroup(groupName) + categoriesForPermission.forEach categoryLoop@ { category -> + val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category] + if (dataCategory == null) { + // Continue to next + return@categoryLoop + } + val typesForCategory = DataTypeConstants.getValidDataTypesForCategory(category) + typesForCategory.forEach typeLoop@ { type -> + val dataType: DataType? = dataCategory.dataTypes[type] + if (dataType == null) { + // Continue to next + return@typeLoop + } + if (dataType.purposeSet.isNotEmpty()) { + return true + } + } + } + + return false + } + /** * Converts a list of LightAppPermGroups into a list of GroupStates */ @@ -666,6 +767,9 @@ class GrantPermissionsViewModel( // is still grantable. return true } + } else if (perm == READ_MEDIA_VISUAL_USER_SELECTED) { + // If USER_SELECTED is granted as fixed, we should immediately show the photo picker + return true } reportRequestResult(perm, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED) @@ -716,21 +820,13 @@ class GrantPermissionsViewModel( } if ((isBackground && group.background.isGrantedExcludingRWROrAllRWR || - !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR)) { - // If FINE location is not granted, do not grant it automatically when COARSE - // location is already granted. - if (group.permGroupName == LOCATION && - group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp - == false) { - return STATE_UNKNOWN - } - + !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR) && + !isSplitGroupLowerGrant(group)) { if (group.permissions[perm]?.isGrantedIncludingAppOp == false) { if (isBackground) { - KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm)) + grantBackgroundRuntimePermissions(app, group, listOf(perm)) } else { - KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm), - group.isOneTime) + grantForegroundRuntimePermissions(app, group, listOf(perm), group.isOneTime) } KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm)) @@ -747,6 +843,38 @@ class GrantPermissionsViewModel( return STATE_UNKNOWN } + /** + * Whether or not this group is a permission where not all permissions are automatically + * granted: There are a set of permissions considered "lower" grants than others. + * + * This method assumes that the + */ + private fun isSplitGroupLowerGrant(group: LightAppPermGroup): Boolean { + if (!group.isGranted) { + return false + } + // If FINE location is not granted, do not grant it automatically when COARSE + // location is already granted. + if (group.permGroupName == LOCATION && + group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == false) { + return true + } + // If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted, + // do not grant. + if (isVisualUserSelectedOnlyGranted(group)) { + return true + } + + return false + } + + private fun isVisualUserSelectedOnlyGranted(group: LightAppPermGroup): Boolean { + return KotlinUtils.isPhotoPickerPromptEnabled() && + group.permGroupName == READ_MEDIA_VISUAL && group.permissions.values.all { + (it.name == READ_MEDIA_VISUAL_USER_SELECTED) || !it.isGrantedIncludingAppOp } && + group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isGrantedIncludingAppOp == true + } + private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int { val isBackground = perm in group.backgroundPermNames var skipGroup = false @@ -756,9 +884,9 @@ class GrantPermissionsViewModel( if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission( app, perm, user.identifier)) { if (isBackground) { - KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm)) + grantBackgroundRuntimePermissions(app, group, listOf(perm)) } else { - KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm)) + grantForegroundRuntimePermissions(app, group, listOf(perm)) } KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true, FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, @@ -831,15 +959,18 @@ class GrantPermissionsViewModel( val foregroundGroupState = groupStates[groupName to false] val backgroundGroupState = groupStates[groupName to true] when (result) { - GrantPermissionsViewHandler.CANCELED -> { + CANCELED -> { if (foregroundGroupState != null) { reportRequestResult(foregroundGroupState.affectedPermissions, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) + foregroundGroupState.state = STATE_SKIPPED } if (backgroundGroupState != null) { reportRequestResult(backgroundGroupState.affectedPermissions, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) + backgroundGroupState.state = STATE_SKIPPED } + requestInfosLiveData.update() return } GRANTED_ALWAYS -> { @@ -866,7 +997,7 @@ class GrantPermissionsViewModel( doNotAskAgain = false) } } - GrantPermissionsViewHandler.GRANTED_ONE_TIME -> { + GRANTED_ONE_TIME -> { if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, affectedForegroundPermissions, granted = true, isOneTime = true, @@ -878,6 +1009,11 @@ class GrantPermissionsViewModel( doNotAskAgain = false) } } + GRANTED_USER_SELECTED, DENIED_MORE_PHOTOS -> { + if (foregroundGroupState != null) { + grantUserSelectedVisualGroupPermissions(foregroundGroupState) + } + } DENIED -> { if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, @@ -905,6 +1041,32 @@ class GrantPermissionsViewModel( } } + private fun grantUserSelectedVisualGroupPermissions(groupState: GroupState) { + val userSelectedPerm = + groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return + val nonSelectedPerms = groupState.affectedPermissions + .filter { it != READ_MEDIA_VISUAL_USER_SELECTED } + if (userSelectedPerm.isImplicit) { + // If the permission is implicit, grant USER_SELECTED as user set, and all other + // permissions as one time, and without app ops. + KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group, + nonSelectedPerms, isOneTime = true, userFixed = false, withoutAppOps = true) + KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group, + listOf(READ_MEDIA_VISUAL_USER_SELECTED)) + onPermissionGrantResultSingleState(groupState, listOf(READ_MEDIA_VISUAL_USER_SELECTED), + granted = true, isOneTime = false, doNotAskAgain = false) + } else { + val setUserFixed = userSelectedPerm.isUserFixed || userSelectedPerm.isUserSet + KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group, + listOf(READ_MEDIA_VISUAL_USER_SELECTED), userFixed = setUserFixed) + KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group, + userFixed = setUserFixed, oneTime = false, filterPermissions = nonSelectedPerms) + } + groupState.state = STATE_ALLOWED + reportButtonClickResult(groupState, true, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED) + } + @SuppressLint("NewApi") private fun onPermissionGrantResultSingleState( groupState: GroupState, @@ -925,11 +1087,11 @@ class GrantPermissionsViewModel( PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED } if (groupState.isBackground) { - KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group, + grantBackgroundRuntimePermissions(app, groupState.group, groupState.affectedPermissions) } else { if (affectedForegroundPermissions == null) { - KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group, + grantForegroundRuntimePermissions(app, groupState.group, groupState.affectedPermissions, isOneTime) // This prevents weird flag state when app targetSDK switches from S+ to R- if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) { @@ -937,7 +1099,7 @@ class GrantPermissionsViewModel( app, groupState.group, true) } } else { - val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, + val newGroup = grantForegroundRuntimePermissions(app, groupState.group, affectedForegroundPermissions, isOneTime) if (!isOneTime || newGroup.isOneTime) { KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup, @@ -969,6 +1131,10 @@ class GrantPermissionsViewModel( } groupState.state = STATE_DENIED } + reportButtonClickResult(groupState, granted, result) + } + + private fun reportButtonClickResult(groupState: GroupState, granted: Boolean, result: Int) { reportRequestResult(groupState.affectedPermissions, result) // group state has changed, reload liveData requestInfosLiveData.update() @@ -1081,6 +1247,29 @@ class GrantPermissionsViewModel( } } + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private fun isHealthPermissionGroup(permGroupName: String): Boolean { + return SdkLevel.isAtLeastU() && HEALTH_PERMISSION_GROUP.equals(permGroupName) + } + + fun handleHealthConnectPermissions(activity: Activity) { + if (activityResultCallback == null) { + activityResultCallback = Consumer { + permGroupsToSkip.add(HEALTH_PERMISSION_GROUP) + requestInfosLiveData.update() + } + val healthPermissions = unfilteredAffectedPermissions.filter { permission -> + isHealthPermission(activity, permission) + }.toTypedArray() + val intent: Intent = Intent(ACTION_MANAGE_HEALTH_PERMISSIONS) + .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions) + .putExtra(Intent.EXTRA_USER, Process.myUserHandle()) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE) + } + } + /** * Send the user directly to the AppPermissionFragment. Used for R+ apps. * @@ -1089,7 +1278,6 @@ class GrantPermissionsViewModel( */ fun sendDirectlyToSettings(activity: Activity, groupName: String) { if (activityResultCallback == null) { - startAppPermissionFragment(activity, groupName) activityResultCallback = Consumer { data -> if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) { // User didn't interact, count against rate limit @@ -1108,9 +1296,36 @@ class GrantPermissionsViewModel( // Update our liveData now that there is a new skipped group requestInfosLiveData.update() } + startAppPermissionFragment(activity, groupName) } } + private fun shouldShowMorePhotosMessage(group: LightAppPermGroup): Boolean { + return isVisualUserSelectedOnlyGranted(group) && + group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == true && + group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU + } + + fun openPhotoPicker(activity: Activity, result: Int) { + if (activityResultCallback != null) { + return + } + val permissions = groupStates[READ_MEDIA_VISUAL to false]?.affectedPermissions ?: return + activityResultCallback = Consumer { data -> + val anySelected = data?.getBooleanExtra(INTENT_PHOTOS_SELECTED, true) == true + if (anySelected) { + onPermissionGrantResult(READ_MEDIA_VISUAL, null, result) + } else { + onPermissionGrantResult(READ_MEDIA_VISUAL, null, CANCELED) + } + logPhotoPickerInteraction(result) + requestInfosLiveData.update() + } + activity.startActivityForResult(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) + .putExtra(Intent.EXTRA_UID, packageInfo.uid) + .setType(KotlinUtils.getMimeTypeForPermissions(permissions)), PHOTO_PICKER_REQUEST_CODE) + } + /** * Send the user to the AppPermissionFragment from a link. Used for Q- apps * @@ -1146,6 +1361,20 @@ class GrantPermissionsViewModel( return "${this::class.java.name}_${groupName}_$isBackground" } + private fun logPhotoPickerInteraction(result: Int) { + val foregroundGroupState = groupStates[READ_MEDIA_VISUAL to false] ?: return + when (result) { + GRANTED_USER_SELECTED -> { + reportRequestResult(foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED) + } + CANCELED -> { + reportRequestResult(foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED) + } + } + } + private fun logSettingsInteraction(groupName: String, result: Int) { val foregroundGroupState = groupStates[groupName to false] val backgroundGroupState = groupStates[groupName to true] @@ -1250,7 +1479,8 @@ class GrantPermissionsViewModel( } companion object { - private const val APP_PERMISSION_REQUEST_CODE = 1 + const val APP_PERMISSION_REQUEST_CODE = 1 + const val PHOTO_PICKER_REQUEST_CODE = 2 private const val STATE_UNKNOWN = 0 private const val STATE_ALLOWED = 1 private const val STATE_DENIED = 2 @@ -1269,10 +1499,11 @@ class GrantPermissionsViewModel( FG_FINE_LOCATION_MESSAGE(4), FG_COARSE_LOCATION_MESSAGE(5), STORAGE_SUPERGROUP_MESSAGE_Q_TO_S(6), - STORAGE_SUPERGROUP_MESSAGE_PRE_Q(7); + STORAGE_SUPERGROUP_MESSAGE_PRE_Q(7), + MORE_PHOTOS_MESSAGE(8) } - fun filterNotificationPermissionIfNeededSync( + fun filterPermissionsIfNeededSync( packageName: String, permissions: Array<String>? ): Array<String>? { @@ -1280,17 +1511,19 @@ class GrantPermissionsViewModel( return null } - try { - val targetSdk = PermissionControllerApplication.get().packageManager + val targetSdk = try { + PermissionControllerApplication.get().packageManager .getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion - if (targetSdk > Build.VERSION_CODES.S_V2) { - return permissions - } } catch (e: PackageManager.NameNotFoundException) { - return permissions + Build.VERSION_CODES.TIRAMISU + } + var permsList = permissions.toMutableList() + + if (targetSdk < Build.VERSION_CODES.TIRAMISU) { + permsList.remove(POST_NOTIFICATIONS) } - return permissions.toList().filter { it != POST_NOTIFICATIONS }.toTypedArray() + return permsList.toTypedArray() } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt index 682ec108f..dd89b7470 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt @@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest import android.app.Application import android.content.Intent +import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Bundle import androidx.fragment.app.Fragment import androidx.lifecycle.AndroidViewModel @@ -73,6 +74,11 @@ class ManageStandardPermissionsViewModel( Utils.navigateToNotificationSettings(fragment.context!!) return } + if (Utils.isHealthPermissionUiEnabled() && + groupName == HEALTH_PERMISSION_GROUP) { + Utils.navigateToHealthConnectSettings(fragment.context!!) + return + } fragment.findNavController().navigateSafe(R.id.manage_to_perm_apps, args) } @@ -99,4 +105,4 @@ class NumCustomPermGroupsWithPackagesLiveData() : override fun onUpdate() { value = customPermGroupPackages.value?.size ?: 0 } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt index 9814a115f..cd1a936f7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt @@ -38,25 +38,25 @@ import androidx.savedstate.SavedStateRegistryOwner import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED -import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED +import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog -import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.CREATION_LOGGED_KEY import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.HAS_SYSTEM_APPS_KEY import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid +import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.navigateSafe @@ -169,8 +169,8 @@ class PermissionAppsViewModel( } } - inner class CategorizedAppsLiveData(groupName: String) - : MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards + inner class CategorizedAppsLiveData(groupName: String) : + MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards Map<Category, List<Pair<String, UserHandle>>>>() { private val packagesUiInfoLiveData = SinglePermGroupPackagesUiInfoLiveData[groupName] diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java index b69fb66a7..4006bcfd3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java @@ -44,8 +44,12 @@ class PermissionGroupPreference extends Preference { setTitle(label); setIcon(tintedIcon); setIntent(managePgIntent); - updateSummary(permissionGroupInfo.getNonSystemGranted(), - permissionGroupInfo.getNonSystemUserSetOrPreGranted()); + updateSummary(permissionGroupInfo); + } + + void updateSummary(PermGroupPackagesUiInfo info) { + updateSummary(info.getNonSystemGranted(), + info.getNonSystemTotal()); } void updateSummary(int granted, int used) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java index 49a01efaf..3541edead 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java @@ -60,8 +60,7 @@ public final class PermissionGroupPreferenceUtils { if (preference == null) { preference = new PermissionGroupPreference(context, info); } else { - preference.updateSummary(info.getNonSystemGranted(), - info.getNonSystemUserSetOrPreGranted()); + preference.updateSummary(info); // Reset the ordering back to default, so that when we add it back it falls into the // right place, and the preferences are ordered as we add them. preference.setOrder(Preference.DEFAULT_ORDER); @@ -99,8 +98,7 @@ public final class PermissionGroupPreferenceUtils { final PermissionGroupPreference preference = (PermissionGroupPreference) preferenceGroup.getPreference(i); final PermGroupPackagesUiInfo info = permissionGroups.get(i); - preference.updateSummary(info.getNonSystemGranted(), - info.getNonSystemUserSetOrPreGranted()); + preference.updateSummary(info); } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt index e65c5c1ff..309b9eba1 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt @@ -47,15 +47,15 @@ import com.android.permissioncontroller.permission.data.PermGroupUsageLiveData import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.micMutedLiveData -import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowLocationIndicators -import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowPermissionsDashboard import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageFragment.PHONE_CALL import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageFragment.VIDEO_CALL import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowLocationIndicators +import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowPermissionsDashboard import com.android.permissioncontroller.permission.utils.Utils -import kotlinx.coroutines.Job import java.time.Instant import kotlin.math.max +import kotlinx.coroutines.Job private const val FIRST_OPENED_KEY = "FIRST_OPENED" private const val CALL_OP_USAGE_KEY = "CALL_OP_USAGE" diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt index 97c5d7596..4e1fc1861 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.model.v33 +package com.android.permissioncontroller.permission.ui.model import android.app.Application import android.content.Context @@ -26,13 +26,13 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.navigateSafe import com.android.settingslib.RestrictedLockUtils @@ -326,4 +326,4 @@ class ReviewPermissionViewModelFactory( @Suppress("UNCHECKED_CAST") return ReviewPermissionsViewModel(app, packageInfo = packageInfo) as T } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt index b3442645d..db79165c3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt @@ -61,9 +61,13 @@ object PermissionUsageControlPreferenceUtils { if (count == 0) { isEnabled = false val permissionUsageSummaryNotUsed = if (show7Days) { - R.string.permission_usage_preference_summary_not_used_7d + StringUtils.getIcuPluralsString(context, + R.string.permission_usage_preference_summary_not_used_in_past_n_days, + 7) } else { - R.string.permission_usage_preference_summary_not_used_24h + StringUtils.getIcuPluralsString(context, + R.string.permission_usage_preference_summary_not_used_in_past_n_hours, + 24) } setSummary(permissionUsageSummaryNotUsed) } else if (SENSOR_DATA_PERMISSIONS.contains(groupName)) { @@ -102,4 +106,4 @@ object PermissionUsageControlPreferenceUtils { } PermissionControllerStatsLog.write(PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, act) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt index 26a6db247..1171cfb0f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt @@ -22,92 +22,66 @@ import android.app.Application import android.app.LoaderManager import android.app.role.RoleManager import android.content.Context +import android.content.pm.ApplicationInfo import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Build import android.os.UserHandle -import android.text.format.DateFormat import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import androidx.preference.Preference -import androidx.preference.PreferenceCategory -import androidx.preference.PreferenceScreen import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.model.AppPermissionGroup +import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.GroupUsage -import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.GroupUsage.AttributionLabelledGroupUsage import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.TimelineUsage import com.android.permissioncontroller.permission.model.v31.PermissionUsages -import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr -import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard +import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel +import com.android.permissioncontroller.permission.utils.PermissionMapping +import com.android.permissioncontroller.permission.utils.StringUtils import com.android.permissioncontroller.permission.utils.SubattributionUtils import com.android.permissioncontroller.permission.utils.Utils -import java.time.Clock import java.time.Instant -import java.time.ZonedDateTime -import java.time.temporal.ChronoUnit -import java.util.Objects import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit.DAYS -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicReference -import java.util.stream.Collectors import kotlin.math.max -/** - * View model for the permission details fragment. - */ +/** View model for the permission details fragment. */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageDetailsViewModel( val application: Application, val roleManager: RoleManager, - private val filterGroup: String, + private val permissionGroup: String, val sessionId: Long ) : ViewModel() { companion object { private const val ONE_HOUR_MS = 3_600_000 private const val ONE_MINUTE_MS = 60_000 - private const val CLUSTER_MINUTES_APART = 1 + private const val CLUSTER_SPACING_MINUTES: Long = 1L private val TIME_7_DAYS_DURATION: Long = DAYS.toMillis(7) private val TIME_24_HOURS_DURATION: Long = DAYS.toMillis(1) - private val ALLOW_CLUSTERING_PERMISSION_GROUPS = listOf( - Manifest.permission_group.LOCATION, - Manifest.permission_group.CAMERA, - Manifest.permission_group.MICROPHONE - ) } - private val filterTimes = mutableListOf<TimeFilterItem>() - - // Truncate to midnight in current timezone. - private val midnightToday = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS) - .toEpochSecond() * 1000L - private val midnightYesterday = ZonedDateTime.now().minusDays(1).truncatedTo(ChronoUnit.DAYS) - .toEpochSecond() * 1000L + private val mTimeFilterItemMs = mutableListOf<TimeFilterItemMs>() init { - initializeTimeFilter(application) + initializeTimeFilterItems(application) } - /** - * Loads permission usages using [PermissionUsages]. Response is returned to the [callback]. - */ + /** Loads permission usages using [PermissionUsages]. Response is returned to the [callback]. */ fun loadPermissionUsages( loaderManager: LoaderManager, permissionUsages: PermissionUsages, callback: PermissionUsages.PermissionsUsagesChangeCallback, filterTimesIndex: Int ) { - val timeFilterItem: TimeFilterItem = filterTimes[filterTimesIndex] - val filterTimeBeginMillis = max(System.currentTimeMillis() - timeFilterItem.time, 0) + val timeFilterItemMs: TimeFilterItemMs = mTimeFilterItemMs[filterTimesIndex] + val filterTimeBeginMillis = max(System.currentTimeMillis() - timeFilterItemMs.timeMs, 0) permissionUsages.load( /* filterPackageName= */ null, /* filterPermissionGroups= */ null, @@ -122,450 +96,466 @@ class PermissionUsageDetailsViewModel( } /** - * Returns whether app subattribution should be shown. - */ - private fun shouldShowSubattributionForApp(appPermissionUsage: AppPermissionUsage): Boolean { - return shouldShowSubattributionInPermissionsDashboard() && - SubattributionUtils.isSubattributionSupported(application, - appPermissionUsage.app.appInfo) - } - - /** - * Create a list of [AppPermissionUsageEntry]s based on the provided data. + * Create a [PermissionUsageDetailsUiData] based on the provided data. * * @param appPermissionUsages data about app permission usages - * @param exemptedPackages packages whose usage should not be included in the out - * @param permApps mutable list of [PermissionApp] for keeping track of information about apps. - * This field is updated as a side effect of running this method. - * @param seenSystemApp mutable field to track whether a system app has recent usage. Updated - * as a side effect of running this method. - * @param showSystemApp whether System apps should be shown + * @param showSystem whether system apps should be shown * @param show7Days whether the last 7 days of history should be shown */ - fun parseUsages( + fun buildPermissionUsageDetailsUiData( appPermissionUsages: List<AppPermissionUsage>, - exemptedPackages: Set<String>, - permApps: MutableList<PermissionApp>, - seenSystemApp: AtomicBoolean, - showSystemApp: Boolean, + showSystem: Boolean, show7Days: Boolean - ): List<AppPermissionUsageEntry> { - val curTime = System.currentTimeMillis() - val showPermissionUsagesDuration = if (is7DayToggleEnabled() && show7Days) { - TIME_7_DAYS_DURATION - } else { - TIME_24_HOURS_DURATION - } - val startTime = (curTime - showPermissionUsagesDuration) - .coerceAtLeast(Instant.EPOCH.toEpochMilli()) - - return appPermissionUsages - .asSequence() - .filter { appUsage: AppPermissionUsage -> - !exemptedPackages.contains(appUsage.packageName) - } - .map { appUsage: AppPermissionUsage -> - filterAndConvert(appUsage, filterGroup) + ): PermissionUsageDetailsUiData { + val showPermissionUsagesDuration = + if (KotlinUtils.is7DayToggleEnabled() && show7Days) { + TIME_7_DAYS_DURATION + } else { + TIME_24_HOURS_DURATION } - .flatten() - .map { usageData: UsageData -> - // Fetch the access time list of the app accesses mFilterGroup permission group - // The DiscreteAccessTime is a Triple of (access time, access duration, - // proxy) of that app - val discreteAccessTimeList: - MutableList<Triple<Long, Long, AppOpsManager.OpEventProxyInfo>> = - mutableListOf() - val timelineUsages = usageData.timelineUsages - val numGroups = timelineUsages.size - for (groupIndex in 0 until numGroups) { - val timelineUsage = timelineUsages[groupIndex] - if (!timelineUsage.hasDiscreteData()) { - continue - } - val isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(timelineUsage.group) - seenSystemApp.set(seenSystemApp.get() || isSystemApp) - if (isSystemApp && !showSystemApp) { - continue - } - for (discreteAccessTime in timelineUsage.allDiscreteAccessTime) { - if (discreteAccessTime.first == 0L || - discreteAccessTime.first < startTime) { - continue - } - discreteAccessTimeList.add(discreteAccessTime) - } - } - discreteAccessTimeList.sortWith { x, y -> y.first.compareTo(x.first) } - if (discreteAccessTimeList.size > 0) { - permApps.add(usageData.app) - } + val startTime = + (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast( + Instant.EPOCH.toEpochMilli()) + val appPermissionTimelineUsages: List<AppPermissionTimelineUsages> = + extractAppPermissionTimelineUsagesForGroup(appPermissionUsages, permissionGroup) + val shouldDisplayShowSystemToggle = + shouldDisplayShowSystemToggle(appPermissionTimelineUsages) + val permissionApps: List<PermissionApp> = + getPermissionAppsWithRecentDiscreteUsage( + appPermissionTimelineUsages, showSystem, startTime) + val appPermissionUsageEntries = + buildDiscreteAccessClusterData(appPermissionTimelineUsages, showSystem, startTime) + + return PermissionUsageDetailsUiData( + permissionApps, shouldDisplayShowSystemToggle, appPermissionUsageEntries) + } - // If the current permission group is not LOCATION or there's only one access - // for the app, return individual entry early. - if (!ALLOW_CLUSTERING_PERMISSION_GROUPS.contains(filterGroup) || - discreteAccessTimeList.size <= 1) { - return@map discreteAccessTimeList.map { time -> - AppPermissionUsageEntry(usageData, time.first, mutableListOf(time)) - } - } + private fun getHistoryPreferenceData( + discreteAccessClusterData: DiscreteAccessClusterData, + ): HistoryPreferenceData { + val context = application + val accessTimeList = + discreteAccessClusterData.discreteAccessDataList.map { p -> p.accessTimeMs } + val durationSummaryLabel = + getDurationSummary(discreteAccessClusterData, accessTimeList, context) + val proxyLabel = getProxyPackageLabel(discreteAccessClusterData) + val subattributionLabel = getSubattributionLabel(discreteAccessClusterData) + val showingSubattribution = + subattributionLabel != null && subattributionLabel!!.isNotEmpty() + val summary = + buildUsageSummary(durationSummaryLabel, proxyLabel, subattributionLabel, context) + + return HistoryPreferenceData( + UserHandle.getUserHandleForUid( + discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.uid), + discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.packageName, + discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.icon, + discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.label, + permissionGroup, + discreteAccessClusterData.endTime, + summary, + showingSubattribution, + accessTimeList, + discreteAccessClusterData.appPermissionTimelineUsages.attributionTags, + sessionId) + } - // Group access time list - val usageEntries = mutableListOf<AppPermissionUsageEntry>() - var ongoingEntry: AppPermissionUsageEntry? = null - for (time in discreteAccessTimeList) { - if (ongoingEntry == null) { - ongoingEntry = AppPermissionUsageEntry(usageData, time.first, - mutableListOf(time)) - } else { - val ongoingAccessTimeList: - MutableList<Triple<Long, Long, AppOpsManager.OpEventProxyInfo>> = - ongoingEntry.clusteredAccessTimeList - if (time.first / ONE_HOUR_MS != - ongoingAccessTimeList[0].first / ONE_HOUR_MS || - ongoingAccessTimeList[ongoingAccessTimeList.size - 1].first / - ONE_MINUTE_MS - time.first / ONE_MINUTE_MS > CLUSTER_MINUTES_APART - ) { - // If the current access time is not in the same hour nor within - // CLUSTER_MINUTES_APART, add the ongoing entry to the usage list - // and start a new ongoing entry. - usageEntries.add(ongoingEntry) - ongoingEntry = AppPermissionUsageEntry(usageData, time.first, - mutableListOf(time)) - } else { - ongoingAccessTimeList.add(time) - } - } - } - ongoingEntry?.let { usageEntries.add(it) } - usageEntries + /** + * Returns whether the provided [AppPermissionUsage] instances contains the provided platform + * permission group. + */ + fun containsPlatformAppPermissionGroup( + appPermissionUsages: List<AppPermissionUsage>, + groupName: String, + ) = appPermissionUsages.extractAllPlatformAppPermissionGroups().any { it.name == groupName } + + /** Extracts a list of [AppPermissionTimelineUsages] for a particular permission group. */ + private fun extractAppPermissionTimelineUsagesForGroup( + appPermissionUsages: List<AppPermissionUsage>, + group: String + ): List<AppPermissionTimelineUsages> = + appPermissionUsages + .filter { !Utils.getExemptedPackages(roleManager).contains(it.packageName) } + .map { appPermissionUsage -> + getAppPermissionTimelineUsages( + appPermissionUsage.app, + appPermissionUsage.groupUsages.firstOrNull { it.group.name == group }) } .flatten() - .sortedWith { x, y -> - // Sort all usage entries by startTime desc, and then by app name. - val timeCompare = java.lang.Long.compare(y.endTime, x.endTime) - if (timeCompare != 0) { - return@sortedWith timeCompare - } - x.usageData.app.label.compareTo(y.usageData.app.label) - } - .toList() - } + + /** Returns whether the show/hide system toggle should be displayed in the UI. */ + private fun shouldDisplayShowSystemToggle( + appPermissionTimelineUsages: List<AppPermissionTimelineUsages>, + ): Boolean = + appPermissionTimelineUsages + .map { it.timelineUsages } + .flatten() + .filter { it.hasDiscreteData() } + .any { it.group.isSystem() } /** - * Render [usages] into the [preferenceScreen] UI. + * Returns a list of [PermissionApp] instances which had recent discrete permission usage + * (recent here refers to usages occurring after the provided start time). */ - fun renderTimelinePreferences( - usages: List<AppPermissionUsageEntry>, - category: AtomicReference<PreferenceCategory>, - preferenceScreen: PreferenceScreen, - historyPreferenceFactory: HistoryPreferenceFactory - ) { - val context = application - var hasADateLabel = false - var lastDateLabel = 0L - usages.forEachIndexed { usageNum, usage -> - val usageTimestamp = usage.endTime - val usageDateLabel = ZonedDateTime.ofInstant(Instant.ofEpochMilli(usageTimestamp), - Clock.systemDefaultZone().zone) - .truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1000L - if (!hasADateLabel || usageDateLabel != lastDateLabel) { - if (hasADateLabel) { - category.set(historyPreferenceFactory.createDayCategoryPreference()) - preferenceScreen.addPreference(category.get()) - } - val formattedDateTitle = DateFormat.getDateFormat(context) - .format(usageDateLabel) - if (usageTimestamp > midnightToday) { - category.get().setTitle(R.string.permission_history_category_today) - } else if (usageTimestamp > midnightYesterday) { - category.get().setTitle(R.string.permission_history_category_yesterday) - } else { - category.get().setTitle(formattedDateTitle) - } - hasADateLabel = true - } + private fun getPermissionAppsWithRecentDiscreteUsage( + appPermissionTimelineUsagesList: List<AppPermissionTimelineUsages>, + showSystem: Boolean, + startTime: Long, + ): List<PermissionApp> = + appPermissionTimelineUsagesList.mapNotNull { appPermissionTimelineUsages -> + if (appPermissionTimelineUsages.timelineUsages + .filter { it.hasDiscreteData() } + .filter { showSystem || !it.group.isSystem() } + .any { timelineUsage -> + timelineUsage.allDiscreteAccessTime.any { it.first >= startTime } + }) + appPermissionTimelineUsages.permissionApp + else null + } - lastDateLabel = usageDateLabel - - val accessTime = DateFormat.getTimeFormat(context).format(usage.endTime) - var durationLong: Long = usage.clusteredAccessTimeList - .map { p -> p.second } - .filter { dur -> dur > 0 } - .sum() - - val accessTimeList: List<Long> = usage.clusteredAccessTimeList.map { p -> p.first } - - // Determine the preference summary. Start with the duration string - var summaryLabel: String? = null - // Since Location accesses are atomic, we manually calculate the access duration - // by comparing the first and last access within the cluster - if (filterGroup == Manifest.permission_group.LOCATION) { - if (accessTimeList.size > 1) { - durationLong = (accessTimeList[0] - accessTimeList[accessTimeList.size - 1]) - - // Similar to other history items, only show the duration if it's longer - // than the clustering granularity. - if (durationLong - >= TimeUnit.MINUTES.toMillis(CLUSTER_MINUTES_APART.toLong()) + 1) { - summaryLabel = getDurationUsedStr(context, durationLong) + /** + * Builds a list of [DiscreteAccessClusterData] from the provided list of + * [AppPermissionTimelineUsages]. + */ + private fun buildDiscreteAccessClusterData( + appPermissionTimelineUsagesList: List<AppPermissionTimelineUsages>, + showSystem: Boolean, + startTime: Long, + ): List<DiscreteAccessClusterData> = + appPermissionTimelineUsagesList + .map { appPermissionTimelineUsages -> + val accessDataList = + extractRecentDiscreteAccessData( + appPermissionTimelineUsages.timelineUsages, showSystem, startTime) + + if (accessDataList.size <= 1) { + return@map accessDataList.map { + DiscreteAccessClusterData( + appPermissionTimelineUsages, it.accessTimeMs, listOf(it)) } } - } else { - // Only show the duration if it is at least (cluster + 1) minutes. Displaying - // times that are the same as the cluster granularity does not convey useful - // information. - if (durationLong != null && - durationLong >= - TimeUnit.MINUTES.toMillis((CLUSTER_MINUTES_APART + 1).toLong())) { - summaryLabel = getDurationUsedStr(context, durationLong) - } - } - var proxyPackageLabel: String? = null - for (clusteredAccessTime in usage.clusteredAccessTimeList) { - val proxy = clusteredAccessTime.third - if (proxy != null && proxy.packageName != null) { - proxyPackageLabel = getPackageLabel( - PermissionControllerApplication.get(), proxy.packageName!!, - UserHandle.getUserHandleForUid(proxy.uid)) - break - } - } - - // fetch the subattribution label for this usage. - var subattributionLabel: String? = null - if (usage.usageData.label != Resources.ID_NULL) { - val attributionLabels: Map<Int, String>? = usage.usageData.app.attributionLabels - if (attributionLabels != null) { - subattributionLabel = attributionLabels[usage.usageData.label] - } + clusterDiscreteAccessData(appPermissionTimelineUsages, accessDataList) } + .flatten() + .sortedWith( + compareBy({ -it.endTime }, { it.appPermissionTimelineUsages.permissionApp.label })) + .toList() - // create subtext string. - val subTextStrings: MutableList<String?> = mutableListOf() - val showingAttribution = subattributionLabel != null && subattributionLabel.isNotEmpty() - if (showingAttribution) { - subTextStrings.add(subattributionLabel) - } - if (proxyPackageLabel != null) { - subTextStrings.add(proxyPackageLabel) - } - if (summaryLabel != null) { - subTextStrings.add(summaryLabel) - } - var subText: String? = null - when (subTextStrings.size) { - 3 -> { - subText = context.getString( - R.string.history_preference_subtext_3, - subTextStrings[0], - subTextStrings[1], - subTextStrings[2]) - } - 2 -> { - subText = context.getString(R.string.history_preference_subtext_2, - subTextStrings[0], - subTextStrings[1]) - } - 1 -> { - subText = subTextStrings[0] - } + /** + * Clusters a list of [DiscreteAccessData] into a list of [DiscreteAccessClusterData] instances. + * + * [DiscreteAccessData] which have accesses sufficiently close together in time will be places + * in the same cluster. + */ + private fun clusterDiscreteAccessData( + appPermissionTimelineUsages: AppPermissionTimelineUsages, + discreteAccessDataList: List<DiscreteAccessData> + ): List<DiscreteAccessClusterData> { + val clusterDataList = mutableListOf<DiscreteAccessClusterData>() + var currentAccessTimeMs: Long = 0 + val currentDiscreteAccessDataList: MutableList<DiscreteAccessData> = mutableListOf() + for (discreteAccessData in discreteAccessDataList) { + if (currentDiscreteAccessDataList.isEmpty()) { + currentDiscreteAccessDataList.add(discreteAccessData) + currentAccessTimeMs = discreteAccessData.accessTimeMs + } else if (!canAccessBeAddedToCluster( + discreteAccessData, currentDiscreteAccessDataList)) { + clusterDataList.add( + DiscreteAccessClusterData( + appPermissionTimelineUsages, + currentAccessTimeMs, + currentDiscreteAccessDataList.toMutableList())) + currentDiscreteAccessDataList.clear() + currentDiscreteAccessDataList.add(discreteAccessData) + currentAccessTimeMs = discreteAccessData.accessTimeMs + } else { + currentDiscreteAccessDataList.add(discreteAccessData) } - - val permissionUsagePreference = historyPreferenceFactory - .createPermissionHistoryPreference( - HistoryPreferenceData( - UserHandle.getUserHandleForUid(usage.usageData.app.getUid()), - usage.usageData.app.packageName, - usage.usageData.app.icon, - usage.usageData.app.label, - filterGroup, accessTime, subText, - showingAttribution, accessTimeList, - usage.usageData.attributionTags, - usageNum == usages.size - 1, - sessionId) - ) - category.get().addPreference(permissionUsagePreference) } + if (currentDiscreteAccessDataList.isNotEmpty()) { + clusterDataList.add( + DiscreteAccessClusterData( + appPermissionTimelineUsages, + currentAccessTimeMs, + currentDiscreteAccessDataList)) + } + return clusterDataList } /** - * Filter the usage data from [appPermissionUsage] into a list of [UsageData]. + * Extract recent [DiscreteAccessData] from a list of [TimelineUsage] instances, and return them + * ordered descending by access time (recent here refers to accesses occurring after the + * provided start time). */ - private fun filterAndConvert( - appPermissionUsage: AppPermissionUsage, - filterGroup: String - ): List<UsageData> { - if (shouldShowSubattributionForApp(appPermissionUsage)) { - return appPermissionUsage.groupUsages - .filter { groupUsage: GroupUsage -> groupUsage.group.name == filterGroup } - .map(GroupUsage::getAttributionLabelledGroupUsages) - .flatten() - .map { labelledGroupUsage: AttributionLabelledGroupUsage -> - UsageData(filterGroup, appPermissionUsage.app, - listOf<TimelineUsage>(labelledGroupUsage), - labelledGroupUsage.label) - } - } - val groupUsages = appPermissionUsage.groupUsages - .filter { groupUsage: GroupUsage -> groupUsage.group.name == filterGroup } - return listOf( - UsageData(filterGroup, appPermissionUsage.app, groupUsages, - Resources.ID_NULL) - ) - } + private fun extractRecentDiscreteAccessData( + timelineUsages: List<TimelineUsage>, + showSystem: Boolean, + startTime: Long + ): List<DiscreteAccessData> = + timelineUsages + .asSequence() + .filter { it.hasDiscreteData() } + .filter { showSystem || !it.group.isSystem() } + .map { getRecentDiscreteAccessData(it, startTime) } + .flatten() + .sortedWith(compareBy { -it.accessTimeMs }) + .toList() /** - * Get an AppPermissionGroup that represents the given permission group (and an arbitrary app). - * - * @param groupName The name of the permission group. - * - * @return an AppPermissionGroup representing the given permission group or null if no such - * AppPermissionGroup is found. + * Extract recent [DiscreteAccessData] from a [TimelineUsage]. (recent here refers to accesses + * occurring after the provided start time). */ - fun getGroup( - groupName: String, - appPermissionUsages: List<AppPermissionUsage> - ): AppPermissionGroup? { - val groups = getOSPermissionGroups(appPermissionUsages) - return groups.firstOrNull { it.name == groupName } + private fun getRecentDiscreteAccessData( + timelineUsage: TimelineUsage, + startTime: Long + ): List<DiscreteAccessData> { + return timelineUsage.allDiscreteAccessTime + .filter { it.first >= startTime } + .map { + DiscreteAccessData( + it.first, + it.second, + it.third, + ) + } } /** - * Get the permission groups declared by the OS. + * Returns whether the provided [DiscreteAccessData] occurred close enough to those in the + * clustered list that it can be added to the cluster + */ + private fun canAccessBeAddedToCluster( + accessData: DiscreteAccessData, + clusteredAccessDataList: List<DiscreteAccessData> + ): Boolean = + accessData.accessTimeMs / ONE_HOUR_MS == + clusteredAccessDataList.first().accessTimeMs / ONE_HOUR_MS && + clusteredAccessDataList.last().accessTimeMs / ONE_MINUTE_MS - + accessData.accessTimeMs / ONE_MINUTE_MS > CLUSTER_SPACING_MINUTES + + /** + * Returns whether the provided [AppPermissionGroup] is considered a system group. * - * TODO: theianchen change the method name to make that clear, - * and return a list of string group names, not AppPermissionGroups. - * @return a list of the permission groups declared by the OS. + * For the purpose of Permissions Hub UI, non user-sensitive [AppPermissionGroup]s are + * considered "system" and should be hidden from the main page unless requested by the user + * through the "show/hide system" toggle. */ - private fun getOSPermissionGroups( - appPermissionUsages: List<AppPermissionUsage> - ): List<AppPermissionGroup> { - val groups: MutableList<AppPermissionGroup> = mutableListOf() - val seenGroups: MutableSet<String> = mutableSetOf() - for (appUsage in appPermissionUsages) { - val groupUsages = appUsage.groupUsages - for (groupUsage in groupUsages) { - if (PermissionMapping.isPlatformPermissionGroup(groupUsage.group.name)) { - if (seenGroups.add(groupUsage.group.name)) { - groups.add(groupUsage.group) - } - } + private fun AppPermissionGroup.isSystem() = !Utils.isGroupOrBgGroupUserSensitive(this) + + /** Returns whether app subattribution should be shown. */ + private fun shouldShowSubattributionForApp(appInfo: ApplicationInfo): Boolean { + return shouldShowSubattributionInPermissionsDashboard() && + SubattributionUtils.isSubattributionSupported(application, appInfo) + } + + /** Returns a summary of the duration the permission was accessed for. */ + private fun getDurationSummary( + usage: DiscreteAccessClusterData, + accessTimeList: List<Long>, + context: Context + ): String? { + if (accessTimeList.isEmpty()) { + return null + } + + var durationMs: Long + + // Since Location accesses are atomic, we manually calculate the access duration + // by comparing the first and last access within the cluster. + if (permissionGroup == Manifest.permission_group.LOCATION) { + durationMs = accessTimeList[0] - accessTimeList[accessTimeList.size - 1] + } else { + durationMs = + usage.discreteAccessDataList.map { it.accessDurationMs }.filter { it > 0 }.sum() + } + // Only show the duration summary if it is at least (CLUSTER_SPACING_MINUTES + 1) minutes. + // Displaying a time that is shorter than the cluster granularity + // (CLUSTER_SPACING_MINUTES) will not convey useful information. + if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) { + return getDurationUsedStr(context, durationMs) + } + + return null + } + + /** Returns the proxied package label if the permission access was proxied. */ + private fun getProxyPackageLabel(usage: DiscreteAccessClusterData): String? = + usage.discreteAccessDataList + .firstOrNull { it.proxy?.packageName != null } + ?.let { + getPackageLabel( + PermissionControllerApplication.get(), + it.proxy!!.packageName!!, + UserHandle.getUserHandleForUid(it.proxy!!.uid)) } + + /** Returns the attribution label for the permission access, if any. */ + private fun getSubattributionLabel(usage: DiscreteAccessClusterData): String? = + if (usage.appPermissionTimelineUsages.label == Resources.ID_NULL) null + else + usage.appPermissionTimelineUsages.permissionApp.attributionLabels?.let { + it[usage.appPermissionTimelineUsages.label] + } + + /** Builds a summary of the permission access. */ + private fun buildUsageSummary( + subattributionLabel: String?, + proxyPackageLabel: String?, + durationSummary: String?, + context: Context + ): String? { + val subTextStrings: MutableList<String?> = mutableListOf() + + subattributionLabel?.let { subTextStrings.add(subattributionLabel) } + proxyPackageLabel?.let { subTextStrings.add(it) } + durationSummary?.let { subTextStrings.add(it) } + return when (subTextStrings.size) { + 3 -> + context.getString( + R.string.history_preference_subtext_3, + subTextStrings[0], + subTextStrings[1], + subTextStrings[2]) + 2 -> + context.getString( + R.string.history_preference_subtext_2, subTextStrings[0], subTextStrings[1]) + 1 -> subTextStrings[0] + else -> null } - return groups } /** - * Initialize the time filter to show the smallest entry greater than the time passed in as an - * argument. If nothing is passed, this simply initializes the possible values. + * Builds a list of [AppPermissionTimelineUsages] from the provided + * [AppPermissionUsage.GroupUsage]. */ - private fun initializeTimeFilter(context: Context) { - filterTimes.add( - TimeFilterItem(Long.MAX_VALUE, - context.getString(R.string.permission_usage_any_time)) - ) - filterTimes.add( - TimeFilterItem(DAYS.toMillis(7), - context.getString(R.string.permission_usage_last_7_days)) - ) - filterTimes.add( - TimeFilterItem(DAYS.toMillis(1), - context.getString(R.string.permission_usage_last_day)) - ) - filterTimes.add( - TimeFilterItem(TimeUnit.HOURS.toMillis(1), - context.getString(R.string.permission_usage_last_hour)) - ) - filterTimes.add( - TimeFilterItem(TimeUnit.MINUTES.toMillis(15), - context.getString(R.string.permission_usage_last_15_minutes)) - ) - filterTimes.add( - TimeFilterItem(TimeUnit.MINUTES.toMillis(1), - context.getString(R.string.permission_usage_last_minute)) - ) + private fun getAppPermissionTimelineUsages( + app: PermissionApp, + groupUsage: AppPermissionUsage.GroupUsage? + ): List<AppPermissionTimelineUsages> { + if (groupUsage == null) { + return listOf() + } - // TODO: theianchen add code for filtering by time here. + if (shouldShowSubattributionForApp(app.appInfo)) { + return groupUsage.attributionLabelledGroupUsages.map { + AppPermissionTimelineUsages( + permissionGroup, app, listOf<TimelineUsage>(it), it.label) + } + } + + return listOf( + AppPermissionTimelineUsages( + permissionGroup, app, listOf<TimelineUsage>(groupUsage), Resources.ID_NULL)) } - /** - * Factory for creating preferences to be added to the screen. - */ - interface HistoryPreferenceFactory { - /** - * Returns a new [PreferenceCategory] representing a day of permission usage. - */ - fun createDayCategoryPreference(): PreferenceCategory - - /** - * Returns a preference representing an app's permission usage, including its timestamp and - * usage details. - */ - fun createPermissionHistoryPreference( - historyPreferenceData: HistoryPreferenceData - ): Preference + /** Extracts to a set all the permission groups declared by the platform. */ + private fun List<AppPermissionUsage>.extractAllPlatformAppPermissionGroups(): + Set<AppPermissionGroup> = + this.flatMap { it.groupUsages } + .map { it.group } + .filter { PermissionMapping.isPlatformPermissionGroup(it.name) } + .toSet() + + /** Initialize all relevant [TimeFilterItemMs] values. */ + private fun initializeTimeFilterItems(context: Context) { + mTimeFilterItemMs.add( + TimeFilterItemMs(Long.MAX_VALUE, context.getString(R.string.permission_usage_any_time))) + mTimeFilterItemMs.add( + TimeFilterItemMs( + DAYS.toMillis(7), + StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 7))) + mTimeFilterItemMs.add( + TimeFilterItemMs( + DAYS.toMillis(1), + StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 1))) + + // TODO: theianchen add code for filtering by time here. } - /** - * Data used to create a preference for an app's permission usage. - */ + /** Data used to create a preference for an app's permission usage. */ data class HistoryPreferenceData( val userHandle: UserHandle, val pkgName: String, val appIcon: Drawable?, val preferenceTitle: String, val permissionGroup: String, - val accessTime: String, + val accessEndTime: Long, val summaryText: CharSequence?, val showingAttribution: Boolean, val accessTimeList: List<Long>, val attributionTags: ArrayList<String>, - val isLastUsage: Boolean, val sessionId: Long ) /** * A class representing a given time, e.g., "in the last hour". * - * @param time the time represented by this object in milliseconds. + * @param timeMs the time represented by this object in milliseconds. * @param label the label to describe the timeframe */ - data class TimeFilterItem( - val time: Long, - val label: String + data class TimeFilterItemMs(val timeMs: Long, val label: String) + + /** + * Class containing all the information needed by the permission usage details fragments to + * render UI. + */ + inner class PermissionUsageDetailsUiData( + /** List of [PermissionApp] instances */ + // Note that these are used only to cache app data for the permission usage details + // fragment, and have no bearing on the UI on the main permission usage page. + val permissionApps: List<PermissionApp>, + /** Whether to show the "show/hide system" toggle. */ + val shouldDisplayShowSystemToggle: Boolean, + /** [DiscreteAccessClusterData] instances ordered for display in UI */ + private val discreteAccessClusterDataList: List<DiscreteAccessClusterData>, + ) { + // Note that the HistoryPreferenceData are not initialized within the + // PermissionUsageDetailsUiData instance as the need to be constructed only after the + // calling fragment loads the necessary PermissionApp instances. We will attempt to remove + // this dependency in b/240978905. + /** Builds a list of [HistoryPreferenceData] to be displayed in the UI. */ + fun getHistoryPreferenceDataList(): List<HistoryPreferenceData> { + return discreteAccessClusterDataList.map { + this@PermissionUsageDetailsViewModel.getHistoryPreferenceData(it) + } + } + } + + /** + * Data class representing a cluster of accesses, to be represented as a single entry in the UI. + */ + data class DiscreteAccessClusterData( + val appPermissionTimelineUsages: AppPermissionTimelineUsages, + val endTime: Long, + val discreteAccessDataList: List<DiscreteAccessData> ) - /** A class representing an app's usage for a group. */ - data class UsageData( - val group: String, + /** Data class representing a discrete permission access. */ + data class DiscreteAccessData( + val accessTimeMs: Long, + val accessDurationMs: Long, + val proxy: AppOpsManager.OpEventProxyInfo? + ) + + /** Data class representing an app's permissions usages for a particular permission group. */ + data class AppPermissionTimelineUsages( + /** Permission group whose usage is being tracked. */ + val permissionGroup: String, // we need a PermissionApp because the loader takes the PermissionApp // object and loads the icon and label information asynchronously - val app: PermissionApp, + /** App whose permissions are being tracked. */ + val permissionApp: PermissionApp, + /** Timeline usages for the given app and permission. */ val timelineUsages: List<TimelineUsage>, val label: Int ) { val attributionTags: java.util.ArrayList<String> - get() = timelineUsages.stream() - .map { obj: TimelineUsage -> obj.attributionTags } - .filter { obj: List<String>? -> Objects.nonNull(obj) } - .flatMap { obj: List<String> -> obj.stream() } - .collect(Collectors.toCollection { ArrayList() }) + get() = ArrayList(timelineUsages.mapNotNull { it.attributionTags }.flatten()) } - - /** - * A class representing an app usage entry in Permission Usage. - */ - data class AppPermissionUsageEntry( - val usageData: UsageData, - val endTime: Long, - val clusteredAccessTimeList: MutableList<Triple<Long, Long, AppOpsManager.OpEventProxyInfo>> - ) } -/** - * Factory for an [PermissionUsageDetailsViewModel] - */ +/** Factory for an [PermissionUsageDetailsViewModel] */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageDetailsViewModelFactory( private val application: Application, @@ -576,7 +566,7 @@ class PermissionUsageDetailsViewModelFactory( override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") - return PermissionUsageDetailsViewModel(application, roleManager, filterGroup, - sessionId) as T + return PermissionUsageDetailsViewModel(application, roleManager, filterGroup, sessionId) + as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt index 7aa6e1728..6287be2be 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt @@ -21,9 +21,6 @@ import android.app.LoaderManager import android.app.role.RoleManager import android.content.Context import android.os.Build -import android.util.ArrayMap -import android.util.ArraySet -import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -31,7 +28,7 @@ import com.android.permissioncontroller.permission.model.AppPermissionGroup import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.model.v31.PermissionUsages -import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled +import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils @@ -39,20 +36,22 @@ import java.time.Instant import java.util.concurrent.TimeUnit import kotlin.math.max +/** [ViewModel] for Permission Usage fragments. */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageViewModel(val roleManager: RoleManager) : ViewModel() { - companion object { - private const val LOG_TAG = "PermissionUsageViewModel" + private val exemptedPackages: Set<String> = Utils.getExemptedPackages(roleManager) + /** Companion object for [PermissionUsageViewModel]. */ + companion object { /** TODO(ewol): Use the config setting to determine amount of time to show. */ private val TIME_FILTER_MILLIS = TimeUnit.DAYS.toMillis(7) private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7) private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1) /** Permission groups that should be hidden from the permissions usage UI. */ private val EXEMPTED_PERMISSION_GROUPS = setOf(Manifest.permission_group.NOTIFICATIONS) - @JvmStatic + /** Map to represent ordering for permission groups in the permissions usage UI. */ val PERMISSION_GROUP_ORDER: Map<String, Int> = mapOf( Manifest.permission_group.LOCATION to 0, @@ -61,6 +60,7 @@ class PermissionUsageViewModel(val roleManager: RoleManager) : ViewModel() { private const val DEFAULT_ORDER = 3 } + /** Loads data from [PermissionUsages] using the [LoaderManager] pattern. */ fun loadPermissionUsages( loaderManager: LoaderManager, permissionUsages: PermissionUsages, @@ -81,177 +81,157 @@ class PermissionUsageViewModel(val roleManager: RoleManager) : ViewModel() { false /*sync*/) } - fun extractUsages( - permissionUsages: List<AppPermissionUsage>, + /** + * Parses the provided list of [AppPermissionUsage] instances to build data for the UI to + * display. + */ + fun buildPermissionUsagesUiData( + appPermissionUsages: List<AppPermissionUsage>, show7Days: Boolean, - showSystem: Boolean - ): Triple<MutableMap<String, Int>, ArrayList<PermissionApp>, Boolean> { + showSystem: Boolean, + context: Context, + ): PermissionUsagesUiData { val curTime = System.currentTimeMillis() val showPermissionUsagesDuration = - if (is7DayToggleEnabled() && show7Days) { + if (KotlinUtils.is7DayToggleEnabled() && show7Days) { TIME_7_DAYS_DURATION } else { TIME_24_HOURS_DURATION } val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli()) - // Permission group to count mapping. - val usages: MutableMap<String, Int> = HashMap() - val permissionGroups: List<AppPermissionGroup> = - getOSPermissionGroupsToDisplay(permissionUsages) - for (i in permissionGroups.indices) { - usages[permissionGroups[i].name] = 0 - } - val permApps = ArrayList<PermissionApp>() - - val exemptedPackages = Utils.getExemptedPackages(roleManager) - - val seenSystemApp: Boolean = - extractPermissionUsage( - exemptedPackages, usages, permApps, startTime, permissionUsages, showSystem) - - return Triple(usages, permApps, seenSystemApp) + val filteredAppPermissionUsages = + appPermissionUsages.filter { !exemptedPackages.contains(it.packageName) } + val displayShowSystemToggle: Boolean = + filteredAppPermissionUsages.displayShowSystemToggle(startTime) + val permissionApps = filteredAppPermissionUsages.getRecentPermissionApps(startTime) + val orderedPermissionGroupsWithUsage = + filteredAppPermissionUsages.buildOrderedPermissionGroupsWithUsageCount( + context, + startTime, + showSystem + ) + + return PermissionUsagesUiData( + permissionApps, + displayShowSystemToggle, + orderedPermissionGroupsWithUsage + ) } - fun createGroupUsagesList( + /** + * Creates an ordered list of [PermissionGroupWithUsageCount] instances to show in the UI, + * representing a mapping of permission groups to the number of apps that recently accessed + * them. + * + * The list is ordered as follows: + * + * 1. Location + * 2. Camera + * 3. Microphone + * 4. Remaining permission groups, ordered alphabetically + */ + private fun List<AppPermissionUsage>.buildOrderedPermissionGroupsWithUsageCount( context: Context, - usages: Map<String, Int> - ): List<Map.Entry<String, Int>> { - val groupUsageNameToLabel: MutableMap<String, CharSequence> = HashMap() - val groupUsagesList: MutableList<Map.Entry<String, Int>> = ArrayList(usages.entries) - val usagesEntryCount = groupUsagesList.size - for (usageEntryIndex in 0 until usagesEntryCount) { - val (key) = groupUsagesList[usageEntryIndex] - groupUsageNameToLabel[key] = getPermGroupLabel(context, key) + startTime: Long, + showSystem: Boolean + ): List<PermissionGroupWithUsageCount> { + val permissionGroupsUsageCountMap: MutableMap<String, Int> = HashMap() + extractPlatformAppPermissionGroupsToDisplay().forEach { + permissionGroupsUsageCountMap[it] = 0 } - groupUsagesList.sortWith { e1: Map.Entry<String, Int>, e2: Map.Entry<String, Int> -> - comparePermissionGroupUsage(e1, e2, groupUsageNameToLabel) + for (appUsage in this) { + appUsage.groupUsages + .filter { showSystem || !it.group.isSystem() } + .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) } + .filter { it.lastAccessTime >= startTime } + .forEach { + permissionGroupsUsageCountMap[it.group.name] = + permissionGroupsUsageCountMap.getOrDefault(it.group.name, 0) + 1 + } } - - return groupUsagesList - } - - private fun comparePermissionGroupUsage( - first: Map.Entry<String, Int>, - second: Map.Entry<String, Int>, - groupUsageNameToLabelMapping: Map<String, CharSequence> - ): Int { - val firstPermissionOrder = PERMISSION_GROUP_ORDER.getOrDefault(first.key, DEFAULT_ORDER) - val secondPermissionOrder = PERMISSION_GROUP_ORDER.getOrDefault(second.key, DEFAULT_ORDER) - return if (firstPermissionOrder != secondPermissionOrder) { - firstPermissionOrder - secondPermissionOrder - } else - groupUsageNameToLabelMapping[first.key] - .toString() - .compareTo(groupUsageNameToLabelMapping[second.key].toString()) + return permissionGroupsUsageCountMap.entries.map { + PermissionGroupWithUsageCount( + it.key, + it.value + ) + } + .sortedWith( + compareBy( + { PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) }, + { getPermGroupLabel(context, it.permGroup).toString() }) + ) } - /** Returns the permission groups declared by the OS that should be displayed in the UI. */ - private fun getOSPermissionGroupsToDisplay( - permissionUsages: List<AppPermissionUsage> - ): List<AppPermissionGroup> { - val groups: MutableList<AppPermissionGroup> = java.util.ArrayList() - val seenGroups: MutableSet<String> = ArraySet() - val numGroups: Int = permissionUsages.size - for (i in 0 until numGroups) { - val appUsage: AppPermissionUsage = permissionUsages.get(i) - val groupUsages = appUsage.groupUsages - val groupUsageCount = groupUsages.size - for (j in 0 until groupUsageCount) { - val groupUsage = groupUsages[j] - if (EXEMPTED_PERMISSION_GROUPS.contains(groupUsage.group.name)) { - continue - } - if (PermissionMapping.isPlatformPermissionGroup(groupUsage.group.name)) { - if (seenGroups.add(groupUsage.group.name)) { - groups.add(groupUsage.group) - } - } + /** Extracts [PermissionApp] where there has been recent permission usage. */ + private fun List<AppPermissionUsage>.getRecentPermissionApps( + startTime: Long, + ): java.util.ArrayList<PermissionApp> { + return ArrayList( + filter { appPermissionUsage -> + appPermissionUsage.groupUsages + .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) } + .any { it.lastAccessTime >= startTime || it.lastAccessTime == 0L } } - } - return groups + .map { it.app }) } /** - * Extract the permission usages from mAppPermissionUsages and put the extracted usages into - * usages and permApps. Returns whether we have seen a system app during the process. - * - * TODO: theianchen It's doing two things at the same method which is violating the SOLID - * principle. We should fix this. - * - * @param exemptedPackages packages that are the role holders for exempted roles - * @param usages an empty List that will be filled with permission usages. - * @param permApps an empty List that will be filled with permission apps. - * @return whether we have seen a system app. + * Returns whether there are any user-sensitive app permission groups with recent usage, and + * therefore if the "show/hide system" toggle needs to be displayed in the UI */ - private fun extractPermissionUsage( - exemptedPackages: Set<String>, - usages: MutableMap<String, Int>, - permApps: java.util.ArrayList<PermissionApp>, + private fun List<AppPermissionUsage>.displayShowSystemToggle( startTime: Long, - permissionUsages: List<AppPermissionUsage>, - showSystem: Boolean ): Boolean { + return flatMap { it.groupUsages } + .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) } + .filter { it.lastAccessTime > startTime && it.lastAccessTime > 0L } + .any { it.group.isSystem() } + } - val mGroupAppCounts: ArrayMap<String?, Int> = ArrayMap() - var seenSystemApp = false - val numApps: Int = permissionUsages.size - for (appNum in 0 until numApps) { - val appUsage: AppPermissionUsage = permissionUsages.get(appNum) - if (exemptedPackages.contains(appUsage.packageName)) { - continue - } - var used = false - val appGroups = appUsage.groupUsages - val numGroups = appGroups.size - for (groupNum in 0 until numGroups) { - val groupUsage = appGroups[groupNum] - val groupName = groupUsage.group.name - if (EXEMPTED_PERMISSION_GROUPS.contains(groupName)) { - continue - } - val lastAccessTime = groupUsage.lastAccessTime - if (lastAccessTime == 0L) { - Log.w( - LOG_TAG, - "Unexpected access time of 0 for ${appUsage.app.key} " + - groupUsage.group.name) - continue - } - if (lastAccessTime < startTime) { - continue - } - val isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(groupUsage.group) - seenSystemApp = seenSystemApp || isSystemApp + /** + * Extracts to a set all the permission groups declared by the platform that should be displayed + * in the UI. + */ + private fun List<AppPermissionUsage>.extractPlatformAppPermissionGroupsToDisplay(): + Set<String> = + this.flatMap { it.groupUsages } + .map { it.group.name } + .filter { PermissionMapping.isPlatformPermissionGroup(it) } + .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it) } + .toSet() - // If not showing system apps, skip. - if (!showSystem && isSystemApp) { - continue - } - used = true - addGroupUser(mGroupAppCounts, groupName) - usages[groupName] = usages.getOrDefault(groupName, 0) + 1 - } - if (used) { - permApps.add(appUsage.app) - addGroupUser(mGroupAppCounts, null) - } - } - return seenSystemApp - } + /** + * Returns whether the [AppPermissionGroup] is considered a system group. + * + * For the purpose of Permissions Hub UI, non user-sensitive [AppPermissionGroup]s are + * considered "system" and should be hidden from the main page unless requested by the user + * through the "show/hide system" toggle. + */ + private fun AppPermissionGroup.isSystem() = !Utils.isGroupOrBgGroupUserSensitive(this) + + /** Data class to hold all the information required to configure the UI. */ + data class PermissionUsagesUiData( + /** List of [PermissionApp] instances */ + // Note that these are used only to cache app data for the permission usage details + // fragment, and have no bearing on the UI on the main permission usage page. + val permissionApps: ArrayList<PermissionApp>, + /** Whether to show the "show/hide system" toggle. */ + val displayShowSystemToggle: Boolean, + // TODO(b/243970988): Consider moving ordering logic to fragment. + /** [PermissionGroupWithUsageCount] instances ordered for display in UI */ + val orderedPermissionGroupsWithUsageCount: List<PermissionGroupWithUsageCount>, + ) - private fun addGroupUser(groupAppCounts: ArrayMap<String?, Int>, app: String?) { - val count: Int? = groupAppCounts[app] - if (count == null) { - groupAppCounts[app] = 1 - } else { - groupAppCounts[app] = count + 1 - } - } + /** + * Data class to associate permission groups with the number of apps that recently accessed + * them. + */ + data class PermissionGroupWithUsageCount(val permGroup: String, val appCount: Int) } -/** Factory for a PermissionUsageViewModel */ +/** Factory for [PermissionUsageViewModel]. */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageViewModelFactory(private val roleManager: RoleManager) : ViewModelProvider.Factory { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt new file mode 100644 index 000000000..9a204f4ef --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt @@ -0,0 +1,340 @@ +/* + * 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.permissioncontroller.permission.ui.model.v31 + +import android.Manifest +import android.app.Application +import android.app.role.RoleManager +import android.os.Build +import android.os.Bundle +import android.os.UserHandle +import androidx.annotation.RequiresApi +import androidx.lifecycle.AbstractSavedStateViewModelFactory +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.savedstate.SavedStateRegistryOwner +import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData +import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData +import com.android.permissioncontroller.permission.data.v31.AllLightPackageOpsLiveData +import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps +import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.Utils +import java.time.Instant +import java.util.concurrent.TimeUnit +import kotlin.math.max + +/** + * [ViewModel] for handheld Permissions Usage UI. + * + * Note that this class replaces [PermissionUsageViewModel] to rely on [LiveData] instead of + * [PermissionUsages] loader. + */ +// TODO(b/257317510): Remove "new" suffix and deprecate PermissionUsageViewModel. +class PermissionUsageViewModelNew( + private val state: SavedStateHandle, + app: Application, +) : AndroidViewModel(app) { + private val roleManager = + Utils.getSystemServiceSafe(app.applicationContext, RoleManager::class.java) + private val exemptedPackages: Set<String> = Utils.getExemptedPackages(roleManager) + + private val mAllLightPackageOpsLiveData = AllLightPackageOpsLiveData(app) + private val appPermGroupUiInfoLiveDataList = + mutableMapOf<AppPermissionId, AppPermGroupUiInfoLiveData>() + + val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false) + val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false) + + /** Updates whether system app permissions usage should be displayed in the UI. */ + fun updateShowSystem(showSystem: Boolean) { + if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) { + state[SHOULD_SHOW_SYSTEM_KEY] = showSystem + } + } + + /** Updates whether 7 days usage or 1 day usage should be displayed in the UI. */ + fun updateShow7Days(show7Days: Boolean) { + if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { + state[SHOULD_SHOW_7_DAYS_KEY] = show7Days + } + } + + /** Builds a [PermissionUsagesUiData] containing all the data necessary to render the UI. */ + private fun buildPermissionUsagesUiData(): PermissionUsagesUiData { + val curTime = System.currentTimeMillis() + val showSystem: Boolean = state[SHOULD_SHOW_SYSTEM_KEY] ?: false + val show7Days: Boolean = state[SHOULD_SHOW_7_DAYS_KEY] ?: false + val showPermissionUsagesDuration = + if (KotlinUtils.is7DayToggleEnabled() && show7Days) { + TIME_7_DAYS_DURATION + } else { + TIME_24_HOURS_DURATION + } + val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli()) + return PermissionUsagesUiData( + mAllLightPackageOpsLiveData.displayShowSystemToggle(startTime), + showSystem, + show7Days, + mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem)) + } + + /** Builds a map of permission groups to the number of apps that recently accessed them. */ + private fun AllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts( + startTime: Long, + showSystem: Boolean, + ): Map<String, Int> { + val permissionUsageCountMap: MutableMap<String, Int> = HashMap() + for (permissionGroup: String in getAllEligiblePermissionGroups()) { + permissionUsageCountMap[permissionGroup] = 0 + } + + val eligibleLightPackageOpsList: List<LightPackageOps> = + getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf() + + for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) { + val permGroupsToLastAccess: List<Map.Entry<String, Long>> = + lightPackageOps.lastPermissionGroupAccessTimesMs.entries + .filterOutExemptedPermissionGroupsFromKeys() + .filterOutSystemAppPermissionsIfNecessary( + showSystem, lightPackageOps.packageName, lightPackageOps.userHandle) + .filterAccessTimeLaterThan(startTime) + val recentlyUsedPermissions: List<String> = permGroupsToLastAccess.map { it.key } + + for (permissionGroup: String in recentlyUsedPermissions) { + permissionUsageCountMap[permissionGroup] = + permissionUsageCountMap.getOrDefault(permissionGroup, 0) + 1 + } + } + return permissionUsageCountMap + } + + /** + * Determines whether there are any system app permissions with recent usage, in which case the + * "show/hide system" toggle should be displayed in the UI. + */ + private fun AllLightPackageOpsLiveData.displayShowSystemToggle(startTime: Long): Boolean { + val eligibleLightPackageOpsList: List<LightPackageOps> = + getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf() + + for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) { + val recentlyUsedPermissions: Set<String> = + lightPackageOps.lastPermissionGroupAccessTimesMs.entries + .filterAccessTimeLaterThan(startTime) + .map { it.key } + .toSet() + if (recentlyUsedPermissions + .filterOutExemptedPermissionGroups() + .containsSystemAppPermission( + lightPackageOps.packageName, lightPackageOps.userHandle)) { + return true + } + } + + return false + } + + /** + * Returns all permission groups tracked in the [AllLightPackageOpsLiveData] eligible for + * display in the UI. + */ + private fun AllLightPackageOpsLiveData.getAllEligiblePermissionGroups(): Set<String> { + val eligibleLightPackageOpsList = + getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf() + + val allPermissionGroups: Set<String> = + eligibleLightPackageOpsList.flatMap { it.lastPermissionGroupAccessTimesMs.keys }.toSet() + + return allPermissionGroups.filterOutExemptedPermissionGroups().toSet() + } + + private fun isAppPermissionSystem(appPermissionId: AppPermissionId) = + appPermGroupUiInfoLiveDataList[appPermissionId]?.value?.isSystem ?: false + + private fun AllLightPackageOpsLiveData.getAllLightPackageOps() = value?.values + + /** + * Filters out accesses earlier than the provided start time from a map of permission last + * accesses. + */ + private fun Collection<Map.Entry<String, Long>>.filterAccessTimeLaterThan(startTime: Long) = + filter { + it.value > startTime + } + + /** + * Filters out system app permissions from a map of permission last accesses, if showSystem is + * false. + */ + private fun Collection<Map.Entry<String, Long>>.filterOutSystemAppPermissionsIfNecessary( + showSystem: Boolean, + packageName: String, + userHandle: UserHandle + ) = filter { + showSystem || !isAppPermissionSystem(AppPermissionId(packageName, userHandle, it.key)) + } + + /** + * Filters out permission groups that are exempt from permission usage tracking from a map of + * permission last accesses. + */ + private fun Collection<Map.Entry<String, Long>>.filterOutExemptedPermissionGroupsFromKeys() = + filter { + !EXEMPTED_PERMISSION_GROUPS.contains(it.key) + } + + /** + * Filters out permission groups that are exempt from permission usage tracking from a map of + * permission last accesses. + */ + private fun Collection<String>.filterOutExemptedPermissionGroups() = filter { + !EXEMPTED_PERMISSION_GROUPS.contains(it) + } + + /** Filters out [LightPackageOps] for apps that are exempt from permission usage tracking. */ + private fun Collection<LightPackageOps>.filterOutExemptedApps() = filter { + !exemptedPackages.contains(it.packageName) + } + + /** + * Returns from a list of permissions whether any permission along with the provided package + * name and user handle are considered a system app permission. + */ + private fun Collection<String>.containsSystemAppPermission( + packageName: String, + userHandle: UserHandle + ) = any { isAppPermissionSystem(AppPermissionId(packageName, userHandle, it)) } + + /** Identifier for an app permission group combination. */ + data class AppPermissionId( + val packageName: String, + val userHandle: UserHandle, + val permissionGroup: String + ) + + /** Data class to hold all the information required to configure the UI. */ + data class PermissionUsagesUiData( + /** Whether to show the "show/hide system" toggle. */ + val displayShowSystemToggle: Boolean, + /** Whether to show system app permissions in the UI. */ + val showSystemAppPermissions: Boolean, + /** Whether to show usage data for 7 days or 1 day. */ + val show7DaysUsage: Boolean, + /** Map instances for display in UI */ + val permissionGroupsWithUsageCount: Map<String, Int>, + ) + + /** LiveData object for [PermissionUsagesUiData]. */ + val permissionUsagesUiLiveData = + object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsagesUiData>() { + + private var appPermGroupListPopulated: Boolean = false + private val getAppPermGroupUiInfoLiveData = { appPermissionId: AppPermissionId -> + AppPermGroupUiInfoLiveData[ + Triple( + appPermissionId.packageName, + appPermissionId.permissionGroup, + appPermissionId.userHandle, + )] + } + + init { + addSource(mAllLightPackageOpsLiveData) { update() } + addSource(showSystemLiveData) { update() } + addSource(show7DaysLiveData) { update() } + } + + override fun onUpdate() { + if (!mAllLightPackageOpsLiveData.isInitialized) { + return + } + + if (appPermGroupUiInfoLiveDataList.isEmpty()) { + val appPermissionIds = mutableListOf<AppPermissionId>() + val allPackages = mAllLightPackageOpsLiveData.value?.keys ?: setOf() + for (packageWithUserHandle: Pair<String, UserHandle> in allPackages) { + val lastPermissionGroupAccessTimesMs = + mAllLightPackageOpsLiveData.value + ?.get(packageWithUserHandle) + ?.lastPermissionGroupAccessTimesMs + ?: mapOf() + + for (permissionGroupToAccess in lastPermissionGroupAccessTimesMs) { + appPermissionIds.add(AppPermissionId( + packageWithUserHandle.first, + packageWithUserHandle.second, + permissionGroupToAccess.key, + )) + } + } + + setSourcesToDifference( + appPermissionIds, + appPermGroupUiInfoLiveDataList, + getAppPermGroupUiInfoLiveData) { + update() + } + appPermGroupListPopulated = true + + return + } + + if (appPermGroupUiInfoLiveDataList.any { !it.value.isInitialized }) { + return + } + + if (isInitialized && appPermGroupUiInfoLiveDataList.any { it.value.isStale }) { + return + } + + val uiData = buildPermissionUsagesUiData() + // We include this check as we don't want UX updates unless the data to be displayed + // has changed. SmartUpdateMediatorLiveData sends updates if the data has changed OR + // if the data has changed from stale to fresh. + if (value != uiData) { + value = uiData + } + } + } + + /** Companion class for [PermissionUsageViewModelNew]. */ + companion object { + private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7) + private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1) + internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem" + internal const val SHOULD_SHOW_7_DAYS_KEY = "show7Days" + + /** Permission groups that should be hidden from the permissions usage UI. */ + private val EXEMPTED_PERMISSION_GROUPS = setOf(Manifest.permission_group.NOTIFICATIONS) + } + + /** Factory for [PermissionUsageViewModelNew]. */ + @RequiresApi(Build.VERSION_CODES.S) + class PermissionUsageViewModelFactory( + val app: Application, + owner: SavedStateRegistryOwner, + defaultArgs: Bundle + ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { + override fun <T : ViewModel?> create( + key: String, + modelClass: Class<T>, + handle: SavedStateHandle + ): T { + @Suppress("UNCHECKED_CAST") return PermissionUsageViewModelNew(handle, app) as T + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt index e263d310b..1d434fe7d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt @@ -235,7 +235,7 @@ class SafetyCenterQsViewModel( } /** - * Factory for a SafetyCenterQsViewModel + * Factory for a SafetyCenterViewModel * * @param app The current application * @param sessionId A session ID used in logs to identify this particular session diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java index 3c7a90b5b..45dd4c1f2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java @@ -146,8 +146,11 @@ public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsVi @Override public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon, - CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, + CharSequence message, CharSequence detailMessage, + CharSequence permissionRationaleMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities) { + // permissionRationaleMessage ignored by television + // TODO: Handle detailMessage mGroupName = groupName; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt index 5f385e064..b21b4cb88 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt @@ -102,7 +102,7 @@ class TvUnusedAppsFragment : SettingsWithHeader(), } private fun createNoUnusedAppsPreference(): Preference { - val preference = Preference(context) + val preference = Preference(requireContext()) preference.title = getString(R.string.zero_unused_apps) preference.key = UNUSED_PREFERENCE_KEY preference.isSelectable = false diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java index 1c0af63cc..ccd1ed42e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java @@ -96,8 +96,11 @@ public final class GrantPermissionsWearViewHandler implements GrantPermissionsVi @Override public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon, - CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, + CharSequence message, CharSequence detailMessage, + CharSequence permissionRationaleMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities) { + // permissionRationaleMessage ignored by wear + // TODO: Handle detailMessage boolean showDoNotAsk = buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON]; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt index 82ad2494b..a7af4948d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt @@ -21,6 +21,8 @@ import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.BACKUP import android.Manifest.permission.POST_NOTIFICATIONS +import android.Manifest.permission.READ_MEDIA_IMAGES +import android.Manifest.permission.READ_MEDIA_VIDEO import android.Manifest.permission_group.NOTIFICATIONS import android.app.ActivityManager import android.app.AppOpsManager @@ -50,6 +52,7 @@ import android.content.res.Resources import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.drawable.Drawable +import android.healthconnect.HealthConnectManager import android.os.Build import android.os.Bundle import android.os.UserHandle @@ -75,16 +78,17 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPerm import com.android.permissioncontroller.permission.model.livedatatypes.PermState import com.android.permissioncontroller.permission.service.LocationAccessCheck import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicReference import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.launch + /** * A set of util functions designed to work with kotlin, though they can work with java, as well. */ @@ -117,6 +121,158 @@ object KotlinUtils { private val ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE + /** Whether to show the Permissions Hub. */ + private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled" + + /** Whether to show the mic and camera icons. */ + private const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled" + + /** Whether to show the location indicators. */ + private const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled" + + /** Whether location accuracy feature is enabled */ + private const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled" + + /** Whether to show 7-day toggle in privacy hub. */ + private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle" + + /** Whether to show permission rationale in permission settings and grant dialog. */ + private const val PRIVACY_PERMISSION_RATIONALE_ENABLED = "privacy_permission_rationale_enabled" + + /** Whether to placeholder safety label data in permission settings and grant dialog. */ + private const val PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED = + "privacy_placeholder_safety_label_data_enabled" + + /** Default location precision */ + private const val PROPERTY_LOCATION_PRECISION = "location_precision" + + /** Whether to show the photo picker option in permission prompts. */ + private const val PROPERTY_PHOTO_PICKER_PROMPT_ENABLED = "photo_picker_prompt_enabled" + + /** + * Whether the Permissions Hub 2 flag is enabled + * + * @return whether the flag is enabled + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun isPermissionsHub2FlagEnabled(): Boolean { + return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_PERMISSIONS_HUB_2_ENABLED, false) + } + /** + * Whether to show the Permissions Dashboard + * + * @return whether to show the Permissions Dashboard. + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun shouldShowPermissionsDashboard(): Boolean { + return isPermissionsHub2FlagEnabled() + } + + /** + * Whether the Camera and Mic Icons are enabled by flag. + * + * @return whether the Camera and Mic Icons are enabled. + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun isCameraMicIconsFlagEnabled(): Boolean { + return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) + } + + /** + * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons + * specifically, are enabled. + * + * @return whether to show the icons. + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun shouldShowCameraMicIndicators(): Boolean { + return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled() + } + + /** + * Whether the location indicators are enabled by flag. + * + * @return whether the location indicators are enabled by flag. + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun isLocationIndicatorsFlagEnabled(): Boolean { + return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_LOCATION_INDICATORS_ENABLED, false) + } + + /** + * Whether to show the location indicators. The location indicators are enable if the + * permission hub, or location indicator specifically are enabled. + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun shouldShowLocationIndicators(): Boolean { + return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled() + } + + /** + * Whether the location accuracy feature is enabled + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun isLocationAccuracyEnabled(): Boolean { + return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_LOCATION_ACCURACY_ENABLED, true) + } + + /** + * Default state of location precision + * true: default is FINE. + * false: default is COARSE. + */ + fun getDefaultPrecision(): Boolean { + return !SdkLevel.isAtLeastS() || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_LOCATION_PRECISION, true) + } + + /** + * Whether we should enable the 7-day toggle in privacy dashboard + * + * @return whether the flag is enabled + */ + @ChecksSdkIntAtLeast(Build.VERSION_CODES.S) + fun is7DayToggleEnabled(): Boolean { + return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PRIVACY_DASHBOARD_7_DAY_TOGGLE, false) + } + + /** + * Whether the Photo Picker Prompt is enabled + * + * @return `true` iff the Location Access Check is enabled. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") + fun isPhotoPickerPromptEnabled(): Boolean { + return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_PHOTO_PICKER_PROMPT_ENABLED, false) + } + + /* + * Whether we should enable the permission rationale in permission settings and grant dialog + * + * @return whether the flag is enabled + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") + fun isPermissionRationaleEnabled(): Boolean { + return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PRIVACY_PERMISSION_RATIONALE_ENABLED, false) + } + + /** + * Whether we should use placeholder safety label data + * + * @return whether the flag is enabled + */ + fun isPlaceholderSafetyLabelDataEnabled(): Boolean { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED, false) + } + /** * Given a Map, and a List, determines which elements are in the list, but not the map, and * vice versa. Used primarily for determining which liveDatas are already being watched, and @@ -385,6 +541,20 @@ object KotlinUtils { } /** + * Return a specific MIME type, if a set of permissions is associated with one + */ + fun getMimeTypeForPermissions(permissions: List<String>): String? { + if (permissions.contains(READ_MEDIA_IMAGES) && !permissions.contains(READ_MEDIA_VIDEO)) { + return "image/*" + } + if (permissions.contains(READ_MEDIA_VIDEO) && !permissions.contains(READ_MEDIA_IMAGES)) { + return "video/*" + } + + return null + } + + /** * Determines if an app is R or above, or if it is Q-, and has auto revoke enabled * * @param app The currenct application @@ -494,9 +664,12 @@ object KotlinUtils { app: Application, group: LightAppPermGroup, filterPermissions: List<String> = group.permissions.keys.toList(), - isOneTime: Boolean = false + isOneTime: Boolean = false, + userFixed: Boolean = false, + withoutAppOps: Boolean = false, ): LightAppPermGroup { - return grantRuntimePermissions(app, group, false, isOneTime, filterPermissions) + return grantRuntimePermissions(app, group, false, isOneTime, userFixed, + withoutAppOps, filterPermissions) } /** @@ -518,7 +691,8 @@ object KotlinUtils { group: LightAppPermGroup, filterPermissions: List<String> = group.permissions.keys.toList() ): LightAppPermGroup { - return grantRuntimePermissions(app, group, true, false, filterPermissions) + return grantRuntimePermissions(app, group, true, false, false, false, + filterPermissions) } private fun grantRuntimePermissions( @@ -526,16 +700,18 @@ object KotlinUtils { group: LightAppPermGroup, grantBackground: Boolean, isOneTime: Boolean = false, - filterPermissions: List<String> = group.permissions.keys.toList() + userFixed: Boolean = false, + withoutAppOps: Boolean = false, + filterPermissions: List<String> = group.permissions.keys.toList(), ): LightAppPermGroup { - val wasOneTime = group.isOneTime val newPerms = group.permissions.toMutableMap() var shouldKillForAnyPermission = false for (permName in filterPermissions) { val perm = group.permissions[permName] ?: continue val isBackgroundPerm = permName in group.backgroundPermNames if (isBackgroundPerm == grantBackground) { - val (newPerm, shouldKill) = grantRuntimePermission(app, perm, isOneTime, group) + val (newPerm, shouldKill) = grantRuntimePermission(app, perm, group, isOneTime, + userFixed, withoutAppOps) newPerms[newPerm.name] = newPerm shouldKillForAnyPermission = shouldKillForAnyPermission || shouldKill } @@ -544,7 +720,7 @@ object KotlinUtils { val user = UserHandle.getUserHandleForUid(group.packageInfo.uid) for (groupPerm in group.allPermissions.values) { var permFlags = groupPerm!!.flags - permFlags = permFlags.clearFlag(PackageManager.FLAG_PERMISSION_AUTO_REVOKED) + permFlags = permFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED) if (groupPerm!!.flags != permFlags) { app.packageManager.updatePermissionFlags(groupPerm!!.name, group.packageInfo.packageName, PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, @@ -582,8 +758,12 @@ object KotlinUtils { * * @param app The current application * @param perm The permission which should be granted. - * @param group An optional app permission group in which to look for background or foreground + * @param group An app permission group in which to look for background or foreground + * @param isOneTime Whether this is a one-time permission grant * permissions + * @param userFixed Whether to mark the permissions as user fixed when granted + * @param withoutAppOps If these permission have app ops associated, and this value is true, + * then do not grant the app op when the permission is granted, and add the REVOKED_COMPAT flag. * * @return a LightPermission and boolean pair <permission with updated state (or the original * state, if it wasn't changed), should kill app> @@ -591,8 +771,10 @@ object KotlinUtils { private fun grantRuntimePermission( app: Application, perm: LightPermission, + group: LightAppPermGroup, isOneTime: Boolean, - group: LightAppPermGroup + userFixed: Boolean = false, + withoutAppOps: Boolean = false ): Pair<LightPermission, Boolean> { val pkgInfo = group.packageInfo val user = UserHandle.getUserHandleForUid(pkgInfo.uid) @@ -624,31 +806,39 @@ object KotlinUtils { shouldKill = true isGranted = true } - newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) + newFlags = if (withoutAppOps) { + newFlags.setFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) + } else { + newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) + } newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) // If this permission affects an app op, ensure the permission app op is enabled // before the permission grant. - if (affectsAppOp) { + if (affectsAppOp && !withoutAppOps) { allowAppOp(app, perm, group) } } // Granting a permission explicitly means the user already // reviewed it so clear the review flag on every grant. - newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) + newFlags = newFlags.clearFlag(FLAG_PERMISSION_REVIEW_REQUIRED) // Update the permission flags - // Now the apps can ask for the permission as the user - // no longer has it fixed in a denied state. - newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_FIXED) - newFlags = newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_SET) - newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_AUTO_REVOKED) + if (!withoutAppOps && !userFixed) { + // Now the apps can ask for the permission as the user + // no longer has it fixed in a denied state. + newFlags = newFlags.clearFlag(FLAG_PERMISSION_USER_FIXED) + newFlags = newFlags.setFlag(FLAG_PERMISSION_USER_SET) + } else if (userFixed) { + newFlags = newFlags.setFlag(FLAG_PERMISSION_USER_FIXED) + } + newFlags = newFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED) newFlags = if (isOneTime) { - newFlags.setFlag(PackageManager.FLAG_PERMISSION_ONE_TIME) + newFlags.setFlag(FLAG_PERMISSION_ONE_TIME) } else { - newFlags.clearFlag(PackageManager.FLAG_PERMISSION_ONE_TIME) + newFlags.clearFlag(FLAG_PERMISSION_ONE_TIME) } // If we newly grant background access to the fine location, double-guess the user some @@ -1196,6 +1386,11 @@ object KotlinUtils { context.getDrawable(android.R.drawable.ic_safety_protection) != null && !context.getString(android.R.string.safety_protection_display_text).isNullOrEmpty() } + + fun addHealthPermissions(context: Context) { + val permissions = HealthConnectManager.getHealthPermissions(context) + PermissionMapping.addHealthPermissionsToPlatform(permissions) + } } /** @@ -1267,4 +1462,4 @@ fun NavController.navigateSafe(destResId: Int, args: Bundle? = null) { navigate(destResId, args) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt index fa7cbec6d..4e70e1bc5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt @@ -19,6 +19,8 @@ package com.android.permissioncontroller.permission.utils import android.Manifest import android.content.pm.PackageManager import android.content.pm.PermissionInfo +import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP +import android.util.Log import com.android.modules.utils.build.SdkLevel /** @@ -27,6 +29,8 @@ import com.android.modules.utils.build.SdkLevel */ object PermissionMapping { + private val LOG_TAG = "PermissionMapping" + @JvmField val SENSOR_DATA_PERMISSIONS: List<String> = listOf( @@ -52,6 +56,8 @@ object PermissionMapping { /** Set of groups that will be able to receive one-time grant */ private val ONE_TIME_PERMISSION_GROUPS: MutableSet<String> = mutableSetOf() + private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf() + init { PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] = @@ -62,6 +68,9 @@ object PermissionMapping { PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALENDAR] = Manifest.permission_group.CALENDAR + // Any updates to the permissions for the SMS permission group must also be made in + // Permissions {@link com.android.role.controller.model.Permissions} in the role + // library PLATFORM_PERMISSIONS[Manifest.permission.SEND_SMS] = Manifest.permission_group.SMS PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_SMS] = Manifest.permission_group.SMS PLATFORM_PERMISSIONS[Manifest.permission.READ_SMS] = Manifest.permission_group.SMS @@ -92,6 +101,11 @@ object PermissionMapping { Manifest.permission_group.READ_MEDIA_VISUAL } + if (SdkLevel.isAtLeastU()) { + PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED] = + Manifest.permission_group.READ_MEDIA_VISUAL + } + PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_FINE_LOCATION] = Manifest.permission_group.LOCATION PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_COARSE_LOCATION] = @@ -114,6 +128,9 @@ object PermissionMapping { Manifest.permission_group.NEARBY_DEVICES } + // Any updates to the permissions for the CALL_LOG permission group must also be made in + // Permissions {@link com.android.role.controller.model.Permissions} in the role + // library PLATFORM_PERMISSIONS[Manifest.permission.READ_CALL_LOG] = Manifest.permission_group.CALL_LOG PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALL_LOG] = Manifest.permission_group.CALL_LOG @@ -274,4 +291,31 @@ object PermissionMapping { fun supportsOneTimeGrant(permissionGroup: String?): Boolean { return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup) } + + /** + * Adds health permissions as platform permissions. + */ + @JvmStatic + fun addHealthPermissionsToPlatform(permissions: Set<String>) { + if (permissions.isEmpty()) { + Log.w(LOG_TAG, "No health connect permissions found.") + return + } + + PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP] = mutableListOf() + + for (permission in permissions) { + PLATFORM_PERMISSIONS[permission] = HEALTH_PERMISSION_GROUP + PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP]?.add(permission) + HEALTH_PERMISSIONS_SET.add(permission) + } + } + + /** + * Returns true if the given permission is a health platform permission. + */ + @JvmStatic + fun isHealthPermission(permissionName: String): Boolean { + return HEALTH_PERMISSIONS_SET.contains(permissionName) + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt new file mode 100644 index 000000000..5fd852bb6 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt @@ -0,0 +1,44 @@ +/* + * 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.permissioncontroller.permission.utils + +import android.Manifest +import com.android.permission.safetylabel.DataCategoryConstants + +/** + * This file contains the canonical mapping of permission and permission group to Safety Label + * categories and types used in the Permission settings screens and grant dialog. It also includes + * methods related to that mapping. + */ +object SafetyLabelPermissionMapping { + + /** + * Get the Safety Label categories pertaining to a specified permission group. + * + * @param groupName the permission group name + * + * @return The categories or an empty list if the group does not have supported and mapped group + * to safety label category + */ + fun getCategoriesForPermissionGroup(groupName: String): List<String> { + return if (groupName == Manifest.permission_group.LOCATION) { + listOf(DataCategoryConstants.CATEGORY_LOCATION) + } else { + emptyList() + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java index 8f5a70ca0..ef036738d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java @@ -34,12 +34,15 @@ import static android.Manifest.permission_group.STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE; import static android.content.Context.MODE_PRIVATE; +import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS; +import static android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS_AND_DATA; import static android.os.UserHandle.myUserId; import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; @@ -87,6 +90,7 @@ import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.ColorRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; @@ -168,6 +172,11 @@ public final class Utils { private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; + /** Whether to show health permission in various permission controller UIs. */ + private static final String PROPERTY_HEALTH_PERMISSION_UI_ENABLED = + "health_permission_ui_enabled"; + + /** How frequently to check permission event store to scrub old data */ public static final String PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS = "permission_events_check_old_frequency_millis"; @@ -923,6 +932,16 @@ public final class Utils { } /** + * Whether we should show health permissions as platform permissions in the various + * permission controller UI. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") + public static boolean isHealthPermissionUiEnabled() { + return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_HEALTH_PERMISSION_UI_ENABLED, true); + } + + /** * Get a device protected storage based shared preferences. Avoid storing sensitive data in it. * * @param context the context to get the shared preferences @@ -1236,6 +1255,28 @@ public final class Utils { } /** + * Navigate to health connect settings for all apps + * @param context The current Context + */ + public static void navigateToHealthConnectSettings(@NonNull Context context) { + Intent healthConnectIntent = new Intent(ACTION_MANAGE_HEALTH_PERMISSIONS); + context.startActivity(healthConnectIntent); + } + + /** + * Navigate to health connect settings for an app + * @param context The current Context + * @param packageName The package's health connect settings to navigate to + */ + public static void navigateToAppHealthConnectSettings(@NonNull Context context, + @NonNull String packageName, @NonNull UserHandle user) { + Intent appHealthConnectIntent = new Intent(ACTION_MANAGE_HEALTH_PERMISSIONS_AND_DATA); + appHealthConnectIntent.putExtra(EXTRA_PACKAGE_NAME, packageName); + appHealthConnectIntent.putExtra(Intent.EXTRA_USER, user); + context.startActivity(appHealthConnectIntent); + } + + /** * Returns if a card should be shown if the sensor is blocked **/ public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) { diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt index e8d98cd53..a1a4873a3 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt @@ -903,13 +903,12 @@ class AccessibilityJobService : JobService() { override fun onCreate() { super.onCreate() - if (DEBUG) { - Log.v(LOG_TAG, "accessibility privacy source job created.") - } + Log.v(LOG_TAG, "accessibility privacy source job created.") mSourceService = AccessibilitySourceService(this) } override fun onStartJob(params: JobParameters?): Boolean { + Log.v(LOG_TAG, "accessibility privacy source job started.") synchronized(mLock) { if (mCurrentJob != null) { Log.v(LOG_TAG, "Accessibility privacy source job already running") diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt index c5e92eed7..4e2228da2 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt @@ -90,8 +90,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock private val TAG = "NotificationListenerCheck" -private const val DEBUG = false - +private const val DEBUG = true const val SC_NLS_SOURCE_ID = "AndroidNotificationListener" @VisibleForTesting const val SC_NLS_DISABLE_ACTION_ID = "disable_nls_component" @@ -545,7 +544,7 @@ internal class NotificationListenerCheckInternal( .setDeleteIntent(deletePendingIntent) .setContentIntent(clickPendingIntent) - if (appLabel.isNotEmpty()) { + if (appLabel != null && appLabel.isNotEmpty()) { val appNameExtras = Bundle() appNameExtras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appLabel.toString()) b.addExtras(appNameExtras) @@ -840,7 +839,7 @@ class NotificationListenerCheckJobService : JobService() { override fun onCreate() { super.onCreate() - + if (DEBUG) Log.d(TAG, "Nls privacy job created") if (!checkNotificationListenerCheckEnabled(this)) { // NotificationListenerCheck not enabled. End job. return @@ -865,6 +864,7 @@ class NotificationListenerCheckJobService : JobService() { * @return `false` if another check is already running, or if SDK Check fails (below T) */ override fun onStartJob(params: JobParameters): Boolean { + if (DEBUG) Log.d(TAG, "Nls privacy job started") if (!checkNotificationListenerCheckEnabled(this)) { // NotificationListenerCheck not enabled. End job. return false diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING index d6f74d31b..3d2d5f888 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING @@ -48,6 +48,16 @@ ] } ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ], "postsubmit": [ { "name": "CtsPermissionTestCases", diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md index 9e867e341..bde9f86f0 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/Role.md +++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md @@ -61,8 +61,10 @@ is optional and defaults to `false`. - `label`: The string resource for the label of the role, e.g. `@string/role_sms_label`, which says "Default SMS app". For default apps, this string will appear in the default app detail page as the title. This attribute is required if the role is `visible`. -- `minSdkVersion`: The minimum SDK version for the role to be available, e.g. `31` for Android S. -This attribute is optional and defaults to `Build.VERSION_CODES.BASE`. +- `maxSdkVersion`: The maximum SDK version for the role to be available (inclusive), e.g. `31` for +Android S. This attribute is optional and defaults to `Build.VERSION_CODES.CUR_DEVELOPMENT`. +- `minSdkVersion`: The minimum SDK version for the role to be available (inclusive), e.g. `31` for +Android S. This attribute is optional and defaults to `Build.VERSION_CODES.BASE`. - `requestDescription`: The string resource for the description in the request role dialog, e.g. `@string/role_sms_request_description`, which says "Gets access to contacts, SMS, phone". This description should describe to the user the privileges that are going to be granted, and should not @@ -86,10 +88,13 @@ defaults to `false`. - `static`: Whether this role is static, i.e. the role will always be assigned to its default holders. This attribute is optional and defaults to `false`. - `systemOnly`: Whether this role only allows system apps to hold it. This attribute is optional and -defaults to `false. +defaults to `false`. - `visible`: Whether this role is visible to users. If a role is invisible (a.k.a. hidden) to users, users won't be able to find it in Settings, and apps won't be able to request it. The role can still be managed by system APIs and shell command. +- `uiBehavior`: Optional name of a [`RoleUiBehavior`](ui/behavior/RoleUiBehavior.java) class to +control certain role UI behavior in Java code, e.g. `DialerRoleUiBehavior`. This can be useful +when the XML syntax cannot express certain UI behavior specific to the role. The following tags can be specified inside a `<role>` tag: @@ -172,6 +177,7 @@ dumpsys role You can also manage the role holders with `cmd role`: ```bash +cmd role get-role-holders [--user USER_ID] ROLE cmd role add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS] cmd role remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS] cmd role clear-role-holders [--user USER_ID] ROLE [FLAGS] diff --git a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING index 43d15a49e..d7718a2f2 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING @@ -8,5 +8,22 @@ } ] } + ], + "mainline-presubmit": [ + { + "name": "CtsRoleTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677748): These two tests currently fails on R base image + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList" + }, + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } ] } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java b/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java index 3f1e7ed0c..567f1c713 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java +++ b/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java @@ -25,6 +25,8 @@ import androidx.annotation.Nullable; import com.android.permissioncontroller.R; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleBehavior; /** * Mixin for {@link RoleBehavior#getConfirmationMessage(Role, String, Context)} diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java b/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java index 07afc6031..90cda72ca 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java +++ b/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java @@ -23,6 +23,9 @@ import android.util.Log; import androidx.annotation.NonNull; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleBehavior; + /** * Mixin for {@link RoleBehavior#isVisibleAsUser(Role, UserHandle, Context)} that returns whether * the role should be visible from a corresponding boolean resource. diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java b/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java index 862cbba2d..ae54154c6 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java +++ b/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java @@ -22,7 +22,7 @@ import android.content.Intent; import androidx.annotation.NonNull; -import com.android.permissioncontroller.role.model.UserDeniedManager; +import com.android.role.controller.model.UserDeniedManager; import java.util.Objects; diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java index fbb9c2e2e..c1c7da46a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java @@ -29,9 +29,10 @@ import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import com.android.permissioncontroller.permission.utils.CollectionUtils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.ArrayList; import java.util.List; @@ -428,8 +429,8 @@ public class RoleControllerServiceImpl extends RoleControllerService { return false; } ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this); - if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo, - Process.myUserHandle(), this)) { + if (applicationInfo == null || !RoleUiBehaviorUtils.isApplicationVisibleAsUser(role, + applicationInfo, Process.myUserHandle(), this)) { return false; } return true; @@ -444,7 +445,8 @@ public class RoleControllerServiceImpl extends RoleControllerService { if (!role.isAvailable(this)) { return false; } - return role.isVisibleAsUser(Process.myUserHandle(), this); + + return RoleUiBehaviorUtils.isVisibleAsUser(role, Process.myUserHandle(), this); } private static boolean checkFlags(int flags, int allowedFlags) { diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java index 7201acffc..db909a47e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java +++ b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java @@ -27,8 +27,10 @@ import androidx.annotation.Nullable; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.service.BaseSearchIndexablesProvider; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleParserInitializer; +import com.android.role.controller.model.Roles; /** * {@link android.provider.SearchIndexablesProvider} for roles. @@ -41,6 +43,12 @@ public class RoleSearchIndexablesProvider extends BaseSearchIndexablesProvider { public static final String ACTION_MANAGE_SPECIAL_APP_ACCESS = "com.android.permissioncontroller.settingssearch.action.MANAGE_SPECIAL_APP_ACCESS"; + @Override + public boolean onCreate() { + RoleParserInitializer.initialize(); + return true; + } + @Nullable @Override public Cursor queryRawData(@Nullable String[] projection) { @@ -53,7 +61,8 @@ public class RoleSearchIndexablesProvider extends BaseSearchIndexablesProvider { long token = Binder.clearCallingIdentity(); try { - if (!role.isAvailable(context) || !role.isVisible(context)) { + if (!role.isAvailable(context) || !RoleUiBehaviorUtils.isVisible(role, + context)) { continue; } } finally { diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java index 0ddb6c3ac..52471cb32 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java @@ -29,10 +29,11 @@ import androidx.fragment.app.Fragment; import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.R; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.ui.auto.AutoDefaultAppFragment; import com.android.permissioncontroller.role.ui.handheld.HandheldDefaultAppFragment; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; /** * Activity for a default app. @@ -86,7 +87,8 @@ public class DefaultAppActivity extends SettingsActivity { finish(); return; } - if (!role.isVisibleAsUser(user, this)) { + + if (!RoleUiBehaviorUtils.isVisibleAsUser(role, user, this)) { Log.e(LOG_TAG, "Role is invisible: " + roleName); finish(); return; diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java index b4e4aaa80..06e5ed264 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java @@ -39,8 +39,9 @@ import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.List; import java.util.Objects; @@ -208,7 +209,8 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat preference.setChecked(checked); if (applicationInfo != null) { - mRole.prepareApplicationPreferenceAsUser(preference, applicationInfo, mUser, context); + RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference, + applicationInfo, mUser, context); } preferenceScreen.addPreference(preference); @@ -238,8 +240,9 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat mViewModel.setNoneDefaultApp(); } else { String packageName = key; - CharSequence confirmationMessage = mRole.getConfirmationMessage(packageName, - requireContext()); + CharSequence confirmationMessage = + RoleUiBehaviorUtils.getConfirmationMessage(mRole, packageName, + requireContext()); if (confirmationMessage != null) { DefaultAppConfirmationDialogFragment.show(packageName, confirmationMessage, this); } else { diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java index 752dfb9cc..3c8af1136 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java @@ -40,8 +40,9 @@ import androidx.preference.PreferenceScreen; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.List; import java.util.Objects; @@ -186,8 +187,8 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo)); preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context)); } - role.preparePreferenceAsUser((TwoTargetPreference) preference, user, context); - + RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference, + user, context); preferenceGroup.addPreference(preference); } } @@ -198,7 +199,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat Context context = requireContext(); Role role = Roles.get(context).get(roleName); UserHandle user = preference.getExtras().getParcelable(Intent.EXTRA_USER); - Intent intent = role.getManageIntentAsUser(user, context); + Intent intent = RoleUiBehaviorUtils.getManageIntentAsUser(role, user, context); if (intent == null) { intent = DefaultAppActivity.createIntent(roleName, user, context); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java index 080652055..c89e1f71e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java @@ -30,7 +30,7 @@ import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import com.android.permissioncontroller.role.model.Role; +import com.android.role.controller.model.Role; import java.util.List; diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java index 0d5216991..5167cfba8 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java @@ -34,10 +34,11 @@ import androidx.fragment.app.FragmentActivity; import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.permissioncontroller.permission.utils.CollectionUtils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; -import com.android.permissioncontroller.role.model.UserDeniedManager; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; +import com.android.role.controller.model.UserDeniedManager; import java.util.List; import java.util.Objects; @@ -109,7 +110,7 @@ public class RequestRoleActivity extends FragmentActivity { return; } - if (!role.isVisible(this)) { + if (!RoleUiBehaviorUtils.isVisible(role, this)) { Log.e(LOG_TAG, "Role is invisible: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java index 091a71c7d..686703a7f 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java @@ -52,10 +52,10 @@ import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; -import com.android.permissioncontroller.role.model.UserDeniedManager; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; +import com.android.role.controller.model.UserDeniedManager; import java.util.ArrayList; import java.util.List; diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java index d93e8f8d6..ae80f997a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java @@ -23,7 +23,7 @@ import androidx.annotation.NonNull; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import com.android.permissioncontroller.role.model.Role; +import com.android.role.controller.model.Role; /** * {@link ViewModel} for a role request. diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java index 8bca5170c..dab709030 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java @@ -20,7 +20,7 @@ import android.content.pm.ApplicationInfo; import androidx.annotation.NonNull; -import com.android.permissioncontroller.role.model.Role; +import com.android.role.controller.model.Role; import java.util.List; diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java index 9742ed923..b9011bd78 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java @@ -29,9 +29,10 @@ import androidx.annotation.WorkerThread; import androidx.lifecycle.LiveData; import com.android.permissioncontroller.AsyncTaskLiveData; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.ArrayList; import java.util.List; @@ -96,7 +97,7 @@ public class RoleListLiveData extends AsyncTaskLiveData<List<RoleItem>> continue; } - if (!role.isVisibleAsUser(mUser, mContext)) { + if (!RoleUiBehaviorUtils.isVisibleAsUser(role, mUser, mContext)) { continue; } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java index 31042f974..3ccb1d8bc 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java @@ -29,8 +29,9 @@ import androidx.annotation.WorkerThread; import androidx.lifecycle.LiveData; import com.android.permissioncontroller.AsyncTaskLiveData; -import com.android.permissioncontroller.role.model.Role; import com.android.permissioncontroller.role.utils.PackageUtils; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; import java.util.ArrayList; import java.util.List; @@ -94,7 +95,8 @@ public class RoleLiveData extends AsyncTaskLiveData<List<Pair<ApplicationInfo, B + qualifyingPackageName); continue; } - if (!mRole.isApplicationVisibleAsUser(qualifyingApplicationInfo, mUser, mContext)) { + if (!RoleUiBehaviorUtils.isApplicationVisibleAsUser(mRole, qualifyingApplicationInfo, + mUser, mContext)) { continue; } boolean isHolderApplication = holderPackageNames.contains(qualifyingPackageName); diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java index c86d48db0..dbd4c7c03 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java @@ -27,8 +27,8 @@ import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; -import com.android.permissioncontroller.role.model.Role; import com.android.permissioncontroller.role.ui.DefaultAppChildFragment; +import com.android.role.controller.model.Role; /** Screen to pick a default app for a particular {@link Role}. */ public class AutoDefaultAppFragment extends AutoSettingsFrameFragment implements diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java new file mode 100644 index 000000000..40bd7a33e --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java @@ -0,0 +1,63 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.role.model.VisibilityMixin; +import com.android.role.controller.model.Role; + +/*** + * Class for UI behavior of Assistant role + */ +public class AssistantRoleUiBehavior implements RoleUiBehavior { + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return VisibilityMixin.isVisible("config_showDefaultAssistant", context); + } + + @Nullable + @Override + public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + boolean isAutomotive = + context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + + if (isAutomotive) { + return null; + } + + return new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS); + } + + @Nullable + @Override + public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, + @NonNull Context context) { + return context.getString(R.string.assistant_confirmation_message); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java new file mode 100644 index 000000000..018b0db41 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java @@ -0,0 +1,37 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.os.UserHandle; + +import androidx.annotation.NonNull; + +import com.android.permissioncontroller.R; +import com.android.role.controller.model.Role; + +/*** + * Class for UI behavior of Browser role + */ +public class BrowserRoleUiBehavior implements RoleUiBehavior { + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_showBrowserRole); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java new file mode 100644 index 000000000..e6b8dabe1 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java @@ -0,0 +1,65 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.UserHandle; +import android.telecom.TelecomManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin; +import com.android.role.controller.model.Role; + +import java.util.Objects; + +/*** + * Class for UI behavior of Dialer role + */ +public class DialerRoleUiBehavior implements RoleUiBehavior { + + @Override + public void prepareApplicationPreferenceAsUser(@NonNull Role role, + @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, @NonNull Context context) { + TelecomManager telecomManager = context.getSystemService(TelecomManager.class); + String systemPackageName = telecomManager.getSystemDialerPackage(); + if (Objects.equals(applicationInfo.packageName, systemPackageName)) { + preference.setSummary(R.string.default_app_system_default); + } else { + preference.setSummary(null); + } + } + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_showDialerRole); + } + + @Nullable + @Override + public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, + @NonNull Context context) { + return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, + context); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java new file mode 100644 index 000000000..8a62ee7eb --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java @@ -0,0 +1,47 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin; +import com.android.permissioncontroller.role.model.VisibilityMixin; +import com.android.role.controller.model.Role; + +/*** + * Class for UI behavior of Emergency role + */ +public class EmergencyRoleUiBehavior implements RoleUiBehavior { + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return VisibilityMixin.isVisible("config_showDefaultEmergency", context); + } + + @Nullable + @Override + public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, + @NonNull Context context) { + return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, + context); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java new file mode 100644 index 000000000..ac8538b9e --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java @@ -0,0 +1,114 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.app.admin.DevicePolicyResources; +import android.app.role.RoleManager; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import android.os.UserHandle; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.utils.CollectionUtils; +import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.role.model.VisibilityMixin; +import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.utils.UserUtils; +import com.android.role.controller.model.HomeRoleBehavior; +import com.android.role.controller.model.Role; + +/*** + * Class for UI behavior of Home role + */ +public class HomeRoleUiBehavior implements RoleUiBehavior { + + private static final String LOG_TAG = HomeRoleUiBehavior.class.getSimpleName(); + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return VisibilityMixin.isVisible("config_showDefaultHome", context); + } + + @Override + public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference, + @NonNull UserHandle user, @NonNull Context context) { + TwoTargetPreference.OnSecondTargetClickListener listener = null; + RoleManager roleManager = context.getSystemService(RoleManager.class); + String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser( + role.getName(), user)); + if (packageName != null) { + Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES) + .setPackage(packageName) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + PackageManager userPackageManager = UserUtils.getUserContext(context, user) + .getPackageManager(); + ResolveInfo resolveInfo = userPackageManager.resolveActivity(intent, 0); + if (resolveInfo != null && resolveInfo.activityInfo != null + && resolveInfo.activityInfo.exported) { + listener = preference2 -> { + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(LOG_TAG, "Cannot start activity for home app preferences", e); + } + }; + } + } + preference.setOnSecondTargetClickListener(listener); + } + + @Override + public boolean isApplicationVisibleAsUser(@NonNull Role role, + @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, + @NonNull Context context) { + // Home is not available for work profile, so we can just use the current user. + return !HomeRoleBehavior.isSettingsApplication(applicationInfo, context); + } + + @Override + public void prepareApplicationPreferenceAsUser(@NonNull Role role, + @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, @NonNull Context context) { + boolean missingWorkProfileSupport = isMissingWorkProfileSupport(applicationInfo, context); + preference.setEnabled(!missingWorkProfileSupport); + preference.setSummary(missingWorkProfileSupport ? Utils.getEnterpriseString(context, + DevicePolicyResources.Strings.DefaultAppSettings + .HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE, + R.string.home_missing_work_profile_support) : null); + } + + private boolean isMissingWorkProfileSupport(@NonNull ApplicationInfo applicationInfo, + @NonNull Context context) { + boolean hasWorkProfile = UserUtils.getWorkProfile(context) != null; + if (!hasWorkProfile) { + return false; + } + boolean isWorkProfileSupported = applicationInfo.targetSdkVersion + >= Build.VERSION_CODES.LOLLIPOP; + return !isWorkProfileSupported; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java new file mode 100644 index 000000000..a71904050 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java @@ -0,0 +1,121 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; + +import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.role.controller.model.Role; + +/*** + * Interface for UI behavior for roles + */ +public interface RoleUiBehavior { + + /** + * Check whether this role should be visible to user. + * + * @param role the role to check for + * @param user the user to check for + * @param context the `Context` to retrieve system services + * + * @return whether this role should be visible to user + */ + default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return true; + } + + /** + * Get the {@link Intent} to manage this role, or {@code null} to use the default UI. + * + * @param role the role to get the intent for + * @param user the user to manage this role for + * @param context the {@code Context} to retrieve system services + * + * @return the {@link Intent} to manage this role, or {@code null} to use the default UI. + */ + @Nullable + default Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return null; + } + + /** + * Prepare a {@link Preference} for this role. + * + * @param role the role to prepare the preference for + * @param preference the {@link Preference} for this role + * @param user the user for this role + * @param context the {@code Context} to retrieve system services + */ + default void preparePreferenceAsUser(@NonNull Role role, + @NonNull TwoTargetPreference preference, + @NonNull UserHandle user, + @NonNull Context context) { + } + + /** + * Check whether a qualifying application should be visible to user. + * + * @param applicationInfo the {@link ApplicationInfo} for the application + * @param user the user for the application + * @param context the {@code Context} to retrieve system services + * + * @return whether the qualifying application should be visible to user + */ + default boolean isApplicationVisibleAsUser(@NonNull Role role, + @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, + @NonNull Context context) { + return true; + } + + /** + * Prepare a {@link Preference} for this role. + * + * @param role the role to prepare the preference for + * @param preference the {@link Preference} for this role + * @param user the user for this role + * @param context the {@code Context} to retrieve system services + */ + default void prepareApplicationPreferenceAsUser(@NonNull Role role, + @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, @NonNull Context context) { + } + + /** + * Get the confirmation message for adding an application as a holder of this role. + * + * @param role the role to get confirmation message for + * @param packageName the package name of the application to get confirmation message for + * @param context the {@code Context} to retrieve system services + * + * @return the confirmation message, or {@code null} if no confirmation is needed + */ + @Nullable + default CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, + @NonNull Context context) { + return null; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java new file mode 100644 index 000000000..9fc9be3d4 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java @@ -0,0 +1,47 @@ +/* + * 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.permissioncontroller.role.ui.behavior; + +import android.content.Context; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin; +import com.android.role.controller.model.Role; + +/*** + * Class for UI behavior of SMS role + */ +public class SmsRoleUiBehavior implements RoleUiBehavior { + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_showSmsRole); + } + + @Nullable + @Override + public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, + @NonNull Context context) { + return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, + context); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java index 2328bb94e..6ed105149 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java @@ -27,11 +27,12 @@ import androidx.fragment.app.Fragment; import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.R; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.ui.SettingsActivity; import com.android.permissioncontroller.role.ui.auto.AutoSpecialAppAccessFragment; import com.android.permissioncontroller.role.ui.specialappaccess.handheld.HandheldSpecialAppAccessFragment; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; /** * Activity for a special app access. @@ -75,7 +76,8 @@ public class SpecialAppAccessActivity extends SettingsActivity { finish(); return; } - if (!role.isVisible(this)) { + + if (!RoleUiBehaviorUtils.isVisible(role, this)) { Log.e(LOG_TAG, "Role is invisible: " + roleName); finish(); return; diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java index 66e1e53ff..d75747b52 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java @@ -36,9 +36,10 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.List; @@ -157,9 +158,8 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat preference.setChecked(isHolderPackage); UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid); - mRole.prepareApplicationPreferenceAsUser(preference, qualifyingApplicationInfo, user, - context); - + RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference, + qualifyingApplicationInfo, user, context); preferenceScreen.addPreference(preference); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java index 52b7aa08d..ec4de84e1 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java @@ -32,10 +32,11 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; -import com.android.permissioncontroller.role.model.Role; -import com.android.permissioncontroller.role.model.Roles; import com.android.permissioncontroller.role.ui.RoleItem; import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; import java.util.List; @@ -110,10 +111,9 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp preference.setPersistent(false); preference.setOnPreferenceClickListener(this); } - - role.preparePreferenceAsUser((TwoTargetPreference) preference, Process.myUserHandle(), + RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference, + Process.myUserHandle(), context); - preferenceScreen.addPreference(preference); } @@ -126,7 +126,7 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp Context context = requireContext(); Role role = Roles.get(context).get(roleName); UserHandle user = Process.myUserHandle(); - Intent intent = role.getManageIntentAsUser(user, context); + Intent intent = RoleUiBehaviorUtils.getManageIntentAsUser(role, user, context); if (intent == null) { intent = SpecialAppAccessActivity.createIntent(roleName, context); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java index 03aa5407e..0cc00abc1 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java @@ -32,11 +32,11 @@ import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import com.android.permissioncontroller.role.model.Role; import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData; import com.android.permissioncontroller.role.ui.RoleLiveData; import com.android.permissioncontroller.role.ui.RoleSortFunction; import com.android.permissioncontroller.role.utils.UserUtils; +import com.android.role.controller.model.Role; import java.util.List; diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java new file mode 100644 index 000000000..e60bc6d76 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java @@ -0,0 +1,151 @@ +/* + * 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.permissioncontroller.role.utils; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; + +import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior; +import com.android.role.controller.model.Role; + +/** + * Utility methods for Role UI behavior + */ +public final class RoleUiBehaviorUtils { + + private static final String LOG_TAG = RoleUiBehaviorUtils.class.getSimpleName(); + + /** + * Get the role ui behavior if available + */ + @Nullable + private static RoleUiBehavior getUiBehavior(@NonNull Role role) { + String uiBehaviorName = role.getUiBehaviorName(); + if (uiBehaviorName == null) { + return null; + } + RoleUiBehavior uiBehavior; + String uiBehaviorClassName = RoleUiBehavior.class.getPackage().getName() + '.' + + uiBehaviorName; + try { + uiBehavior = (RoleUiBehavior) Class.forName(uiBehaviorClassName).newInstance(); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + Log.e(LOG_TAG, "Unable to instantiate UI behavior: " + uiBehaviorClassName, e); + return null; + } + return uiBehavior; + } + + /** + * @see RoleUiBehavior#isVisibleAsUser + */ + public static boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return role.isVisible(); + } + return role.isVisible() && uiBehavior.isVisibleAsUser(role, user, context); + } + + /** + * Check whether this role should be visible to user, for current user. + * + * @param context the `Context` to retrieve system services + * + * @return whether this role should be visible to user. + */ + public static boolean isVisible(@NonNull Role role, @NonNull Context context) { + return isVisibleAsUser(role, Process.myUserHandle(), context); + } + + + /** + * @see RoleUiBehavior#getManageIntentAsUser + */ + @Nullable + public static Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return null; + } + return uiBehavior.getManageIntentAsUser(role, user, context); + } + + /** + * @see RoleUiBehavior#preparePreferenceAsUser + */ + public static void preparePreferenceAsUser(@NonNull Role role, + @NonNull TwoTargetPreference preference, @NonNull UserHandle user, + @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return; + } + uiBehavior.preparePreferenceAsUser(role, preference, user, context); + } + + /** + * @see RoleUiBehavior#isApplicationVisibleAsUser + */ + public static boolean isApplicationVisibleAsUser(@NonNull Role role, + @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, + @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return true; + } + return uiBehavior.isApplicationVisibleAsUser(role, applicationInfo, user, context); + } + + /** + * @see RoleUiBehavior#prepareApplicationPreferenceAsUser + */ + public static void prepareApplicationPreferenceAsUser(@NonNull Role role, + @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return; + } + uiBehavior.prepareApplicationPreferenceAsUser(role, preference, applicationInfo, user, + context); + } + + /** + * @see RoleUiBehavior#getConfirmationMessage + */ + @Nullable + public static CharSequence getConfirmationMessage(@NonNull Role role, + @NonNull String packageName, @NonNull Context context) { + RoleUiBehavior uiBehavior = getUiBehavior(role); + if (uiBehavior == null) { + return null; + } + return uiBehavior.getConfirmationMessage(role, packageName, context); + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java index cd7a6b8a5..e89470ff6 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java @@ -39,16 +39,41 @@ public class UserUtils { /** * Check whether a user is a profile. * - * @param user the user to check + * @param user the user to check * @param context the {@code Context} to retrieve system services - * * @return whether the user is a profile */ public static boolean isProfile(@NonNull UserHandle user, @NonNull Context context) { + return isManagedProfile(user, context) || isCloneProfile(user, context); + } + + /** + * Check whether a user is a managed profile. + * + * @param user the user to check + * @param context the {@code Context} to retrieve system services + * @return whether the user is a managed profile + */ + public static boolean isManagedProfile(@NonNull UserHandle user, @NonNull Context context) { Context userContext = getUserContext(context, user); UserManager userUserManager = userContext.getSystemService(UserManager.class); - return userUserManager.isManagedProfile(user.getIdentifier()) || ( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && userUserManager.isCloneProfile()); + return userUserManager.isManagedProfile(user.getIdentifier()); + } + + /** + * Check whether a user is a clone profile. + * + * @param user the user to check + * @param context the {@code Context} to retrieve system services + * @return whether the user is a clone profile + */ + public static boolean isCloneProfile(@NonNull UserHandle user, @NonNull Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + return false; + } + Context userContext = getUserContext(context, user); + UserManager userUserManager = userContext.getSystemService(UserManager.class); + return userUserManager.isCloneProfile(); } /** @@ -92,11 +117,7 @@ public class UserUtils { if (Process.myUserHandle().equals(user)) { return context; } else { - try { - return context.createPackageContextAsUser(context.getPackageName(), 0, user); - } catch (PackageManager.NameNotFoundException doesNotHappen) { - throw new IllegalStateException(doesNotHappen); - } + return context.createContextAsUser(user, 0); } } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING index e2eb5ef5d..c702ee852 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING @@ -24,5 +24,15 @@ { "name": "SafetyCenterFunctionalTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java index eb4a84a14..86612d35a 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java @@ -19,7 +19,8 @@ package com.android.permissioncontroller.safetycenter.service; import static android.app.job.JobScheduler.RESULT_SUCCESS; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.safetycenter.SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED; -import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT; +import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER; +import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC; import static com.android.permissioncontroller.Constants.SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID; @@ -39,6 +40,10 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.modules.utils.build.SdkLevel; + +import java.time.Duration; + /** * Uses {@link android.app.job.JobScheduler} to schedule one-off calls to {@link * SafetyCenterManager#refreshSafetySources} after boot completed and after safety center is @@ -51,12 +56,12 @@ import androidx.annotation.Nullable; // TODO(b/243537828): Consider disabling this during other tests in case it makes them flakey public final class SafetyCenterBackgroundRefreshJobService extends JobService { private static final String TAG = "SafetyCenterBackgroundR"; - + private static final Duration DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL = Duration.ofDays(1); /** * Schedules a one-off call to {@link SafetyCenterManager#refreshSafetySources} to be run when * the device is idle. */ - public static void scheduleOneOffBackgroundRefresh( + public static void schedulePeriodicBackgroundRefresh( @NonNull Context context, @Nullable String actionString) { if (!(ACTION_BOOT_COMPLETED.equals(actionString) @@ -70,20 +75,31 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService { return; } - Log.v(TAG, "Scheduling a one-off background refresh."); + Log.v(TAG, "Scheduling a periodic background refresh."); JobScheduler jobScheduler = requireNonNull(context.getSystemService(JobScheduler.class)); + // TODO(b/256610767): Consider adding setPriority(JobInfo.PRIORITY_LOW) and + // setRequiresCharging(true) JobInfo.Builder builder = (new JobInfo.Builder( SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID, new ComponentName( context, SafetyCenterBackgroundRefreshJobService.class))) - .setRequiresDeviceIdle(true); + .setRequiresDeviceIdle(true) + .setPeriodic(getPeriodicBackgroundRefreshInterval().toMillis()); int scheduleResult = jobScheduler.schedule(builder.build()); if (scheduleResult != RESULT_SUCCESS) { - Log.e(TAG, "Could not schedule background refresh, scheduleResult=" + scheduleResult); + Log.e( + TAG, + "Could not schedule the periodic background refresh, scheduleResult=" + + scheduleResult); } } + private static Duration getPeriodicBackgroundRefreshInterval() { + // TODO(b/256610767): Add DeviceConfig/phenotype flag + return DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL; + } + @Override public boolean onStartJob(@NonNull JobParameters params) { // background thread not required, PC APK makes all API calls in main thread @@ -91,25 +107,28 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService { SafetyCenterManager safetyCenterManager = requireNonNull(this.getSystemService(SafetyCenterManager.class)); if (safetyCenterManager.isSafetyCenterEnabled()) { - // TODO(b/243523521): Use the correct refresh reason depending on which intent the - // receiver receives - safetyCenterManager.refreshSafetySources(REFRESH_REASON_DEVICE_REBOOT); + safetyCenterManager.refreshSafetySources(getRefreshReason()); } return false; // job is no longer running } + private static int getRefreshReason() { + if (SdkLevel.isAtLeastU()) { + return REFRESH_REASON_PERIODIC; + } + return REFRESH_REASON_OTHER; + } + @Override public boolean onStopJob(@NonNull JobParameters params) { return false; // never want job to be rescheduled } - /** - * Schedules a background refresh on boot completed and when safety center is enabled. - */ + /** Schedules a periodic background refresh. */ public static final class SetupSafetyCenterBackgroundRefreshReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { - scheduleOneOffBackgroundRefresh(context, intent.getAction()); + schedulePeriodicBackgroundRefresh(context, intent.getAction()); } } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java index ce32108d2..4c6bee746 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java @@ -46,6 +46,7 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; import com.android.safetycenter.internaldata.SafetyCenterIds; @@ -102,9 +103,19 @@ public class IssueCardPreference extends Preference implements ComparablePrefere ((TextView) holder.findViewById(R.id.issue_card_title)).setText(mIssue.getTitle()); ((TextView) holder.findViewById(R.id.issue_card_summary)).setText(mIssue.getSummary()); + CharSequence attributionTitle = SdkLevel.isAtLeastU() ? mIssue.getAttributionTitle() : null; + TextView attributionTitleTextView = + (TextView) holder.findViewById(R.id.issue_card_attribution_title); + if (TextUtils.isEmpty(attributionTitle)) { + attributionTitleTextView.setVisibility(View.GONE); + } else { + attributionTitleTextView.setText(attributionTitle); + attributionTitleTextView.setVisibility(View.VISIBLE); + } CharSequence subtitle = mIssue.getSubtitle(); TextView subtitleTextView = (TextView) holder.findViewById(R.id.issue_card_subtitle); CharSequence contentDescription; + // TODO(b/257972736): Add a11y support for attribution title. if (TextUtils.isEmpty(subtitle)) { subtitleTextView.setVisibility(View.GONE); contentDescription = diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java index c9024cd30..cd3e3639d 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java @@ -15,8 +15,10 @@ */ package com.android.permissioncontroller.safetycenter.ui; +import static android.content.Intent.ACTION_SAFETY_CENTER; import static android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID; import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_SIGNAL_NOTIFICATION_INTERACTION; import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_CLICKED; @@ -65,7 +67,13 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { } Fragment frag; - if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) { + if (SafetyCenterUiFlags.getShowSubpages() + && getIntent().getAction().equals(ACTION_SAFETY_CENTER) + && getIntent().hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) { + frag = + new SafetyCenterSubpageFragment( + getIntent().getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)); + } else if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) { setTitle(R.string.privacy_controls_title); frag = PrivacyControlsFragment.newInstance(); } else { diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java index 92001b5fb..ce70d0fc0 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java @@ -38,7 +38,6 @@ import android.safetycenter.SafetyCenterErrorDetails; import android.safetycenter.SafetyCenterIssue; import android.safetycenter.SafetyCenterStaticEntry; import android.safetycenter.SafetyCenterStaticEntryGroup; -import android.safetycenter.SafetyCenterStatus; import android.util.Log; import android.widget.Toast; @@ -58,6 +57,7 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; +import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; import com.android.safetycenter.internaldata.SafetyCenterIds; import com.android.safetycenter.resources.SafetyCenterResourcesContext; @@ -174,11 +174,6 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa mSafetyStatusPreference = requireNonNull(getPreferenceScreen().findPreference(SAFETY_STATUS_KEY)); - // TODO: Use real strings here, or set more sensible defaults in the layout - mSafetyStatusPreference.setSafetyStatus( - new SafetyCenterStatus.Builder("Looks good", "") - .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN) - .build()); mSafetyStatusPreference.setViewModel(mViewModel); mIssuesGroup = getPreferenceScreen().findPreference(ISSUES_GROUP_KEY); @@ -191,6 +186,7 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa mStaticEntriesGroup = null; } + mViewModel.getStatusUiLiveData().observe(this, this::updateStatus); mViewModel.getSafetyCenterUiLiveData().observe(this, this::renderSafetyCenterData); mViewModel.getErrorLiveData().observe(this, this::displayErrorDetails); @@ -256,6 +252,21 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa return mViewModel; } + private void updateStatus(StatusUiData statusUiData) { + if (mIsQuickSettingsFragment) { + SafetyCenterResourcesContext safetyCenterResourcesContext = + new SafetyCenterResourcesContext(requireContext()); + boolean hasPendingActions = + safetyCenterResourcesContext + .getStringByName("overall_severity_level_ok_review_summary") + .equals(statusUiData.getOriginalSummary().toString()); + + statusUiData = statusUiData.copyForPendingActions(hasPendingActions); + } + + mSafetyStatusPreference.setData(statusUiData); + } + private void renderSafetyCenterData(@Nullable SafetyCenterUiData uiData) { if (uiData == null) return; SafetyCenterData data = uiData.getSafetyCenterData(); @@ -267,37 +278,13 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa return; } - mSafetyStatusPreference.setSafetyData(data); - // TODO(b/208212820): Only update entries that have changed since last // update, rather than deleting and re-adding all. updateIssues(context, data.getIssues(), uiData.getResolvedIssues()); + if (!mIsQuickSettingsFragment) { updateSafetyEntries(context, data.getEntriesOrGroups()); updateStaticSafetyEntries(context, data.getStaticEntryGroups()); - } else { - SafetyCenterResourcesContext safetyCenterResourcesContext = - new SafetyCenterResourcesContext(context); - boolean hasSettingsToReview = - safetyCenterResourcesContext - .getStringByName("overall_severity_level_ok_review_summary") - .equals(data.getStatus().getSummary().toString()); - setPendingActionState(hasSettingsToReview); - } - } - - /** Determine if there are pending actions and set pending actions state */ - private void setPendingActionState(boolean hasSettingsToReview) { - if (hasSettingsToReview) { - mSafetyStatusPreference.setHasPendingActions( - true, - l -> { - mViewModel.navigateToSafetyCenter( - this, NavigationSource.QUICK_SETTINGS_TILE); - mViewModel.getInteractionLogger().record(Action.REVIEW_SETTINGS_CLICKED); - }); - } else { - mSafetyStatusPreference.setHasPendingActions(false, null); } } @@ -334,7 +321,9 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa boolean isFirstElement = i == 0; boolean isLastElement = i == size - 1; - if (entry != null) { + if (SafetyCenterUiFlags.getShowSubpages() && group != null) { + mEntriesGroup.addPreference(new SafetyHomepageEntryPreference(context, group)); + } else if (entry != null) { addTopLevelEntry(context, entry, isFirstElement, isLastElement); } else if (group != null) { addGroupEntries(context, group, isFirstElement, isLastElement); diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java index 265845aec..d6015ffae 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java @@ -174,22 +174,24 @@ public class SafetyCenterQsFragment extends Fragment { securitySettings.setOnClickListener( (v) -> mSafetyCenterViewModel.navigateToSafetyCenter( - this, NavigationSource.QUICK_SETTINGS_TILE)); - TextView securitySettingsText = - securitySettings.findViewById(R.id.toggle_sensor_name); + mContext, NavigationSource.QUICK_SETTINGS_TILE)); + TextView securitySettingsText = securitySettings.findViewById(R.id.toggle_sensor_name); securitySettingsText.setText(R.string.settings); securitySettingsText.setSelected(true); securitySettings.findViewById(R.id.toggle_sensor_status).setVisibility(View.GONE); - ImageView securitySettingsIcon = - securitySettings.findViewById(R.id.toggle_sensor_icon); - securitySettingsIcon.setImageDrawable(Utils.applyTint(mContext, - mContext.getDrawable(R.drawable.ic_safety_center_shield), - android.R.attr.textColorPrimaryInverse)); + ImageView securitySettingsIcon = securitySettings.findViewById(R.id.toggle_sensor_icon); + securitySettingsIcon.setImageDrawable( + Utils.applyTint( + mContext, + mContext.getDrawable(R.drawable.ic_safety_center_shield), + android.R.attr.textColorPrimaryInverse)); securitySettings.findViewById(R.id.arrow_icon).setVisibility(View.VISIBLE); ((ImageView) securitySettings.findViewById(R.id.arrow_icon)) - .setImageDrawable(Utils.applyTint(mContext, - mContext.getDrawable(R.drawable.ic_chevron_right), - android.R.attr.textColorSecondaryInverse)); + .setImageDrawable( + Utils.applyTint( + mContext, + mContext.getDrawable(R.drawable.ic_chevron_right), + android.R.attr.textColorSecondaryInverse)); ViewCompat.replaceAccessibilityAction( securitySettings, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK, diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt new file mode 100644 index 000000000..43c371620 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt @@ -0,0 +1,75 @@ +/* + * 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.permissioncontroller.safetycenter.ui + +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.os.Bundle +import android.safetycenter.SafetyCenterEntryGroup +import android.safetycenter.SafetyCenterEntryOrGroup +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.preference.PreferenceFragmentCompat +import com.android.permissioncontroller.R +import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory +import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData +import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel + +/** A fragment that represents a generic subpage in Safety Center. */ +@RequiresApi(UPSIDE_DOWN_CAKE) +class SafetyCenterSubpageFragment(private val sourceGroupId: String) : PreferenceFragmentCompat() { + + private lateinit var viewModel: SafetyCenterViewModel + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.safety_center_subpage, rootKey) + + viewModel = + ViewModelProvider( + requireActivity(), + LiveSafetyCenterViewModelFactory(requireActivity().getApplication())) + .get(SafetyCenterViewModel::class.java) + viewModel.safetyCenterUiLiveData.observe( + this, + Observer { uiData: SafetyCenterUiData? -> this.renderSafetyCenterEntryGroup(uiData) }) + } + + private fun renderSafetyCenterEntryGroup(uiData: SafetyCenterUiData?) { + val entryGroup = getMatchingGroup(uiData, sourceGroupId) + if (entryGroup == null) { + Log.w(TAG, "$sourceGroupId doesn't match any of the existing SafetySourcesGroup IDs") + requireActivity().getSupportFragmentManager().popBackStack() + return + } + requireActivity().setTitle(entryGroup.title) + } + + private fun getMatchingGroup( + uiData: SafetyCenterUiData?, + sourceGroupId: String + ): SafetyCenterEntryGroup? { + val entryOrGroups: List<SafetyCenterEntryOrGroup>? = + uiData?.safetyCenterData?.entriesOrGroups + val entryGroups = entryOrGroups?.mapNotNull { it.entryGroup } + return entryGroups?.find { it.id == sourceGroupId } + } + + companion object { + private const val TAG: String = "SafetyCenterSubpageFragment" + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt new file mode 100644 index 000000000..053a77787 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt @@ -0,0 +1,35 @@ +/* + * 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.permissioncontroller.safetycenter.ui + +import android.provider.DeviceConfig +import androidx.core.os.BuildCompat + +/** A class to access the Safety Center UI related {@link DeviceConfig} flags. */ +object SafetyCenterUiFlags { + private const val PROPERTY_SHOW_SUBPAGES = "safety_center_show_subpages" + + /** + * Returns whether to show subpages in the Safety Center UI for Android-U instead of the + * expand-and-collapse list implementation. + */ + @JvmStatic + fun getShowSubpages(): Boolean { + return BuildCompat.isAtLeastU() && + DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_SHOW_SUBPAGES, false) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt index 78837dd07..75e8800c1 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt @@ -27,9 +27,7 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel import com.android.permissioncontroller.safetycenter.ui.view.SafetyEntryGroupView -/** - * A preference that displays a visual representation of a {@link SafetyCenterEntryGroup}. - */ +/** A preference that displays a visual representation of a {@link SafetyCenterEntryGroup}. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class SafetyGroupPreference( context: Context, @@ -43,8 +41,6 @@ class SafetyGroupPreference( private val onCollapsedListener: (String) -> Unit ) : Preference(context), ComparablePreference { - val groupId = group.id - init { layoutResource = R.layout.preference_group } @@ -53,24 +49,22 @@ class SafetyGroupPreference( super.onBindViewHolder(holder) (holder?.itemView as? SafetyEntryGroupView)?.showGroup( - group, - isExpanded, - isFirstCard, - isLastCard, - getTaskIdForEntry, - viewModel, - onExpandedListener, - onCollapsedListener - ) + group, + isExpanded, + isFirstCard, + isLastCard, + getTaskIdForEntry, + viewModel, + onExpandedListener, + onCollapsedListener) } override fun isSameItem(other: Preference): Boolean = - other is SafetyGroupPreference && - TextUtils.equals(group.id, other.group.id) + other is SafetyGroupPreference && TextUtils.equals(group.id, other.group.id) override fun hasSameContents(other: Preference): Boolean = - other is SafetyGroupPreference && - group == other.group && - isFirstCard == other.isFirstCard && - isLastCard == other.isLastCard -}
\ No newline at end of file + other is SafetyGroupPreference && + group == other.group && + isFirstCard == other.isFirstCard && + isLastCard == other.isLastCard +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt new file mode 100644 index 000000000..583f3f381 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt @@ -0,0 +1,63 @@ +/* + * 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.permissioncontroller.safetycenter.ui + +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.safetycenter.SafetyCenterEntryGroup +import android.safetycenter.SafetyCenterManager +import android.text.TextUtils +import androidx.annotation.RequiresApi +import androidx.preference.Preference +import java.util.Objects + +/** + * A preference that displays a visual representation of a {@link SafetyCenterEntryGroup} on the + * Safety Center homepage. + */ +@RequiresApi(UPSIDE_DOWN_CAKE) +internal class SafetyHomepageEntryPreference( + context: Context, + private val entryGroup: SafetyCenterEntryGroup +) : Preference(context), ComparablePreference { + + init { + setTitle(entryGroup.title) + setSummary(entryGroup.summary) + setIcon( + SeverityIconPicker.selectIconResId( + entryGroup.severityLevel, entryGroup.severityUnspecifiedIconType)) + + // TODO(b/260822348): Check if there is a better way to open the subpage fragment + val intent = Intent(Intent.ACTION_SAFETY_CENTER) + intent.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID, entryGroup.id) + setIntent(intent) + } + + override fun isSameItem(preference: Preference): Boolean = + preference is SafetyHomepageEntryPreference && entryGroup.id == preference.entryGroup.id + + override fun hasSameContents(preference: Preference): Boolean = + preference is SafetyHomepageEntryPreference && + Objects.equals(entryGroup.id, preference.entryGroup.id) && + TextUtils.equals(entryGroup.title, preference.entryGroup.title) && + TextUtils.equals(entryGroup.summary, preference.entryGroup.summary) && + entryGroup.severityLevel == preference.entryGroup.severityLevel && + entryGroup.severityUnspecifiedIconType == + preference.entryGroup.severityUnspecifiedIconType +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java index f081017b4..ee2e7c73d 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java @@ -17,9 +17,6 @@ package com.android.permissioncontroller.safetycenter.ui; import static android.os.Build.VERSION_CODES.TIRAMISU; -import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING; -import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK; -import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION; import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN; import android.content.Context; @@ -28,11 +25,9 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; -import android.safetycenter.SafetyCenterData; import android.safetycenter.SafetyCenterStatus; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -46,6 +41,7 @@ import androidx.preference.PreferenceViewHolder; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; +import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; import com.google.android.material.button.MaterialButton; @@ -57,10 +53,8 @@ import kotlin.Pair; /** Preference which displays a visual representation of {@link SafetyCenterStatus}. */ @RequiresApi(TIRAMISU) public class SafetyStatusPreference extends Preference implements ComparablePreference { - private static final String TAG = "SafetyStatusPreference"; - @Nullable private SafetyCenterStatus mStatus; - @Nullable private View.OnClickListener mReviewSettingsOnClickListener; + @Nullable private StatusUiData mStatus; @Nullable private SafetyCenterViewModel mViewModel; @NonNull @@ -74,8 +68,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref new TextFadeAnimator(List.of(R.id.status_title, R.id.status_summary)); private boolean mFirstBind = true; - private boolean mHasPendingActions; - private boolean mHasIssues; public SafetyStatusPreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -103,8 +95,16 @@ public class SafetyStatusPreference extends Preference implements ComparablePref MaterialButton pendingActionsRescanButton = (MaterialButton) holder.findViewById(R.id.pending_actions_rescan_button); View reviewSettingsButton = holder.findViewById(R.id.review_settings_button); - if (mHasPendingActions) { - reviewSettingsButton.setOnClickListener(mReviewSettingsOnClickListener); + if (mStatus.hasPendingActions()) { + reviewSettingsButton.setOnClickListener( + l -> { + requireViewModel() + .navigateToSafetyCenter( + context, NavigationSource.QUICK_SETTINGS_TILE); + requireViewModel() + .getInteractionLogger() + .record(Action.REVIEW_SETTINGS_CLICKED); + }); reviewSettingsButton.setVisibility(View.VISIBLE); } else { reviewSettingsButton.setVisibility(View.GONE); @@ -112,15 +112,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref rescanButton = updateRescanButtonUi(rescanButton, pendingActionsRescanButton); setRescanButtonState(rescanButton); - int contentDescriptionResId = - R.string.safety_status_preference_title_and_summary_content_description; holder.findViewById(R.id.status_title_and_summary) - .setContentDescription( - getContext() - .getString( - contentDescriptionResId, - mStatus.getTitle(), - mStatus.getSummary())); + .setContentDescription(mStatus.getContentDescription(context)); rescanButton.setOnClickListener( unused -> { @@ -143,7 +136,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref View safetyProtectionSectionView = holder.findViewById(R.id.safety_protection_section_view); if (KotlinUtils.INSTANCE.shouldShowSafetyProtectionResources(context)) { // Hide the Safety Protection branding if there are any issue cards - safetyProtectionSectionView.setVisibility(mHasIssues ? View.GONE : View.VISIBLE); + safetyProtectionSectionView.setVisibility( + mStatus.hasIssues() ? View.GONE : View.VISIBLE); } if (safetyProtectionSectionView.getVisibility() == View.GONE) { holder.itemView.setPaddingRelative( @@ -165,7 +159,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref private void updateStatusText(TextView title, TextView summary) { if (mFirstBind) { title.setText(mStatus.getTitle()); - summary.setText(getSummaryText()); + summary.setText(mStatus.getSummary(getContext())); } runTextAnimationIfNeeded(title, summary); } @@ -173,7 +167,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref private void updateStatusIcon(ImageView statusImage, View rescanButton) { int severityLevel = mStatus.getSeverityLevel(); - boolean isRefreshing = isRefreshInProgress(); + boolean isRefreshing = mStatus.isRefreshInProgress(); boolean shouldStartScanAnimation = isRefreshing && !mIsScanAnimationRunning; boolean shouldEndScanAnimation = !isRefreshing && mIsScanAnimationRunning; boolean shouldChangeIcon = mSettledSeverityLevel != severityLevel; @@ -202,13 +196,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref return; } String titleText = mStatus.getTitle().toString(); - String summaryText = getSummaryText().toString(); + String summaryText = mStatus.getSummary(getContext()).toString(); boolean titleEquals = titleView.getText().toString().equals(titleText); boolean summaryEquals = summaryView.getText().toString().equals(summaryText); - Runnable onFinish = () -> { - mIsTextChangeAnimationRunning = false; - runTextAnimationIfNeeded(titleView, summaryView); - }; + Runnable onFinish = + () -> { + mIsTextChangeAnimationRunning = false; + runTextAnimationIfNeeded(titleView, summaryView); + }; mIsTextChangeAnimationRunning = !titleEquals || !summaryEquals; if (!titleEquals && !summaryEquals) { Pair<TextView, String> titleChange = new Pair<>(titleView, titleText); @@ -221,20 +216,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } } - private CharSequence getSummaryText() { - if (mHasPendingActions) { - return getContext().getString(R.string.safety_center_qs_status_summary); - } else { - return mStatus.getSummary().toString(); - } - } - - private boolean isRefreshInProgress() { - int refreshStatus = mStatus.getRefreshStatus(); - return refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS - || refreshStatus == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS; - } - private void startScanningAnimation(ImageView statusImage) { mIsScanAnimationRunning = true; statusImage.setImageResource( @@ -264,7 +245,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref new Animatable2.AnimationCallback() { @Override public void onAnimationEnd(Drawable drawable) { - if (mIsScanAnimationRunning && isRefreshInProgress()) { + if (mIsScanAnimationRunning && mStatus.isRefreshInProgress()) { scanningAnim.start(); } else { scanningAnim.clearAnimationCallbacks(); @@ -303,8 +284,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref @Override public void onAnimationEnd(Drawable drawable) { super.onAnimationEnd(drawable); - finishScanAnimation( - statusImage, rescanButton); + finishScanAnimation(statusImage, rescanButton); } }); animatedDrawable.start(); @@ -350,7 +330,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } mSettledSeverityLevel = mStatus.getSeverityLevel(); - statusImage.setImageResource(toStatusImageResId(mSettledSeverityLevel)); + statusImage.setImageResource(mStatus.getStatusImageResId()); } private void handleQueuedAction(ImageView statusImage) { @@ -369,7 +349,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref */ private MaterialButton updateRescanButtonUi( MaterialButton rescanButton, MaterialButton pendingActionsRescanButton) { - if (mHasPendingActions) { + if (mStatus.hasPendingActions()) { rescanButton.setVisibility(View.GONE); pendingActionsRescanButton.setVisibility(View.VISIBLE); return pendingActionsRescanButton; @@ -379,14 +359,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref return rescanButton; } - void setSafetyStatus(SafetyCenterStatus status) { - mStatus = status; - safeNotifyChanged(); - } - - void setSafetyData(SafetyCenterData data) { - mHasIssues = data.getIssues().size() > 0; - mStatus = data.getStatus(); + void setData(StatusUiData statusUiData) { + mStatus = statusUiData; safeNotifyChanged(); } @@ -398,50 +372,15 @@ public class SafetyStatusPreference extends Preference implements ComparablePref return Objects.requireNonNull(mViewModel); } - /** - * System has pending actions when the user security and privacy signals are deemed to be safe, - * but the user has previously dismissed some warnings that may need their review - */ - void setHasPendingActions(boolean hasPendingActions, View.OnClickListener listener) { - mHasPendingActions = hasPendingActions; - mReviewSettingsOnClickListener = listener; - safeNotifyChanged(); - } - private void setRescanButtonState(View rescanButton) { - rescanButton.setVisibility(shouldShowRescanButton() ? View.VISIBLE : View.GONE); - rescanButton.setEnabled(!isRefreshInProgress()); - } - - private boolean shouldShowRescanButton() { - int severityLevel = mStatus.getSeverityLevel(); - return !mHasIssues - && !mHasPendingActions // hides the second button in QS to keep the UI clean - && (severityLevel == OVERALL_SEVERITY_LEVEL_OK - || severityLevel == OVERALL_SEVERITY_LEVEL_UNKNOWN); + rescanButton.setVisibility(mStatus.shouldShowRescanButton() ? View.VISIBLE : View.GONE); + rescanButton.setEnabled(!mStatus.isRefreshInProgress()); } // Calling notifyChanged while recyclerview is scrolling or computing layout will result in an // IllegalStateException. Post to handler to wait for UI to settle. private void safeNotifyChanged() { - new Handler(Looper.getMainLooper()).post(() -> notifyChanged()); - } - - private static int toStatusImageResId(int overallSeverityLevel) { - switch (overallSeverityLevel) { - case OVERALL_SEVERITY_LEVEL_UNKNOWN: - case OVERALL_SEVERITY_LEVEL_OK: - return R.drawable.safety_status_info; - case OVERALL_SEVERITY_LEVEL_RECOMMENDATION: - return R.drawable.safety_status_recommendation; - case OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING: - return R.drawable.safety_status_warn; - default: - Log.w( - TAG, - String.format("Unexpected OverallSeverityLevel: %s", overallSeverityLevel)); - return R.drawable.safety_status_info; - } + new Handler(Looper.getMainLooper()).post(this::notifyChanged); } @Override @@ -456,6 +395,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref return false; } SafetyStatusPreference other = (SafetyStatusPreference) preference; - return Objects.equals(mStatus, other.mStatus) && mHasIssues == other.mHasIssues; + return Objects.equals(mStatus, other.mStatus); } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt index c0d4e8f41..9d2b808b4 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.safetycenter.ui.model import android.app.Application +import android.content.Context import android.content.Intent import android.content.Intent.ACTION_SAFETY_CENTER import android.os.Build @@ -30,9 +31,9 @@ import android.util.Log import androidx.annotation.MainThread import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat.getMainExecutor -import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.safetycenter.ui.InteractionLogger @@ -44,6 +45,8 @@ import com.android.safetycenter.internaldata.SafetyCenterIds class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName + override val statusUiLiveData: LiveData<StatusUiData> + get() = Transformations.map(safetyCenterUiLiveData) { StatusUiData(it.safetyCenterData) } override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData @@ -117,14 +120,14 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { _errorLiveData.value = null } - override fun navigateToSafetyCenter(fragment: Fragment, navigationSource: NavigationSource?) { + override fun navigateToSafetyCenter(context: Context, navigationSource: NavigationSource?) { val intent = Intent(ACTION_SAFETY_CENTER) if (navigationSource != null) { navigationSource.addToIntent(intent) } - fragment.startActivity(intent) + context.startActivity(intent) } override fun pageOpen() { @@ -219,9 +222,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { private fun determineResolvedIssues(nextIssueIds: Set<IssueId>): Map<IssueId, ActionId> { // Any previously in-flight issue that does not appear in the incoming SafetyCenterData // is considered resolved. - return issuesPendingResolution.filterNot { issue -> - nextIssueIds.contains(issue.key) - } + return issuesPendingResolution.filterNot { issue -> nextIssueIds.contains(issue.key) } } private fun shouldEndScan(nextData: SafetyCenterData): Boolean = @@ -260,9 +261,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { } } -/** - * Returns inflight issues pending resolution - */ +/** Returns inflight issues pending resolution */ private fun SafetyCenterData.getInFlightIssues(): Map<IssueId, ActionId> = issues .map { issue -> diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt index 291079ce9..d59c2b5a3 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt @@ -17,12 +17,12 @@ package com.android.permissioncontroller.safetycenter.ui.model import android.app.Application +import android.content.Context import android.os.Build import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterErrorDetails import android.safetycenter.SafetyCenterIssue import androidx.annotation.RequiresApi -import androidx.fragment.app.Fragment import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import com.android.permissioncontroller.safetycenter.ui.InteractionLogger @@ -31,6 +31,7 @@ import com.android.permissioncontroller.safetycenter.ui.NavigationSource @RequiresApi(Build.VERSION_CODES.TIRAMISU) abstract class SafetyCenterViewModel(protected val app: Application) : AndroidViewModel(app) { + abstract val statusUiLiveData: LiveData<StatusUiData> abstract val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> abstract val errorLiveData: LiveData<SafetyCenterErrorDetails> abstract val interactionLogger: InteractionLogger @@ -56,7 +57,7 @@ abstract class SafetyCenterViewModel(protected val app: Application) : AndroidVi abstract fun clearError() abstract fun navigateToSafetyCenter( - fragment: Fragment, + context: Context, navigationSource: NavigationSource? = null ) diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt new file mode 100644 index 000000000..beeda213c --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt @@ -0,0 +1,80 @@ +package com.android.permissioncontroller.safetycenter.ui.model + +import android.content.Context +import android.safetycenter.SafetyCenterData +import android.safetycenter.SafetyCenterStatus +import android.util.Log +import com.android.permissioncontroller.R + +/** UI model representation of a Status Card. */ +data class StatusUiData( + private val status: SafetyCenterStatus, + @get:JvmName("hasIssues") val hasIssues: Boolean = false, + @get:JvmName("hasPendingActions") val hasPendingActions: Boolean = false +) { + + constructor( + safetyCenterData: SafetyCenterData + ) : this(safetyCenterData.status, hasIssues = safetyCenterData.issues.size > 0) + + // For convenience use in Java. + fun copyForPendingActions(hasPendingActions: Boolean) = + copy(hasPendingActions = hasPendingActions) + + private companion object { + val TAG: String = StatusUiData::class.java.simpleName + } + + val title: CharSequence by status::title + val originalSummary: CharSequence by status::summary + val severityLevel: Int by status::severityLevel + + val statusImageResId: Int + get() = + when (severityLevel) { + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN, + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK -> R.drawable.safety_status_info + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION -> + R.drawable.safety_status_recommendation + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> + R.drawable.safety_status_warn + else -> { + Log.w(TAG, "Unexpected OverallSeverityLevel: $severityLevel") + R.drawable.safety_status_info + } + } + + fun getSummary(context: Context): CharSequence { + return if (hasPendingActions) { + // Use a different string for the special quick-settings-only hasPendingActions state. + context.getString(R.string.safety_center_qs_status_summary) + } else { + originalSummary + } + } + + fun getContentDescription(context: Context): CharSequence { + return context.getString( + R.string.safety_status_preference_title_and_summary_content_description, + title, + getSummary(context)) + } + + val isRefreshInProgress: Boolean + get() = + when (status.refreshStatus) { + SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS, + SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS -> true + else -> false + } + + fun shouldShowRescanButton(): Boolean { + return !hasIssues && + !hasPendingActions && + when (severityLevel) { + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK, + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN -> true + else -> false + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AppOp.java b/PermissionController/src/com/android/role/controller/model/AppOp.java index 700cf7fe7..3156880a4 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/AppOp.java +++ b/PermissionController/src/com/android/role/controller/model/AppOp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.ApplicationInfo; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java b/PermissionController/src/com/android/role/controller/model/AppOpPermissions.java index 25d8dc942..0f5e07055 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java +++ b/PermissionController/src/com/android/role/controller/model/AppOpPermissions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.app.AppOpsManager; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AssistantRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/AssistantRoleBehavior.java index 66dd8ccb9..70f9b1b80 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/AssistantRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/AssistantRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.app.ActivityManager; import android.app.role.RoleManager; @@ -27,7 +27,6 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Process; import android.os.UserHandle; -import android.provider.Settings; import android.service.voice.VoiceInteractionService; import android.util.ArraySet; import android.util.AttributeSet; @@ -37,7 +36,6 @@ import android.util.Xml; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.permissioncontroller.R; import com.android.permissioncontroller.role.utils.UserUtils; import org.xmlpull.v1.XmlPullParserException; @@ -78,33 +76,6 @@ public class AssistantRoleBehavior implements RoleBehavior { return !UserUtils.isProfile(user, context); } - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return VisibilityMixin.isVisible("config_showDefaultAssistant", context); - } - - @Nullable - @Override - public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - boolean isAutomotive = - context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - - if (isAutomotive) { - return null; - } - - return new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS); - } - - @Nullable - @Override - public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, - @NonNull Context context) { - return context.getString(R.string.assistant_confirmation_message); - } - @Nullable @Override public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user, diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/AutomotiveRoleBehavior.java index 3596c7d91..74187f292 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/AutomotiveRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.PackageManager; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/BrowserRoleBehavior.java index 9d4d9e08b..adccfc6db 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/BrowserRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.Intent; @@ -29,7 +29,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; -import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.CollectionUtils; import com.android.permissioncontroller.role.utils.PackageUtils; import com.android.permissioncontroller.role.utils.UserUtils; @@ -136,8 +135,8 @@ public class BrowserRoleBehavior implements RoleBehavior { // #grantDefaultPermissionsToDefaultBrowser(java.lang.String, int) if (SdkLevel.isAtLeastS()) { if (PackageUtils.isSystemPackage(packageName, context)) { - Permissions.grant(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false, false, - true, false, context); + Permissions.grant(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false, true, + false, false, context); } } } @@ -146,15 +145,9 @@ public class BrowserRoleBehavior implements RoleBehavior { public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) { if (SdkLevel.isAtLeastT()) { if (PackageUtils.isSystemPackage(packageName, context)) { - Permissions.revoke(packageName, SYSTEM_BROWSER_PERMISSIONS, false, true, false, + Permissions.revoke(packageName, SYSTEM_BROWSER_PERMISSIONS, true, false, false, context); } } } - - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return context.getResources().getBoolean(R.bool.config_showBrowserRole); - } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceAppStreamingRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceAppStreamingRoleBehavior.java index ca4af2355..b556a0d38 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceAppStreamingRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceAppStreamingRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceComputerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceComputerRoleBehavior.java index 1d9409f1f..741a44e15 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceComputerRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceComputerRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceWatchRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceWatchRoleBehavior.java index 75675fb00..19294226e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceWatchRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceWatchRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DevicePolicyManagementRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DevicePolicyManagementRoleBehavior.java index 8232847e0..a423f9d19 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/DevicePolicyManagementRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/DevicePolicyManagementRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.app.admin.DevicePolicyManager; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DialerRoleBehavior.java index 36be7e88a..188a0009c 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/DialerRoleBehavior.java @@ -14,25 +14,19 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; -import android.content.pm.ApplicationInfo; import android.os.UserHandle; -import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.Preference; import com.android.modules.utils.build.SdkLevel; -import com.android.permissioncontroller.R; import com.android.permissioncontroller.role.utils.PackageUtils; import java.util.Arrays; import java.util.List; -import java.util.Objects; /** * Class for behavior of the dialer role. @@ -58,33 +52,6 @@ public class DialerRoleBehavior implements RoleBehavior { } @Override - public void prepareApplicationPreferenceAsUser(@NonNull Role role, - @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, - @NonNull UserHandle user, @NonNull Context context) { - TelecomManager telecomManager = context.getSystemService(TelecomManager.class); - String systemPackageName = telecomManager.getSystemDialerPackage(); - if (Objects.equals(applicationInfo.packageName, systemPackageName)) { - preference.setSummary(R.string.default_app_system_default); - } else { - preference.setSummary(null); - } - } - - @Nullable - @Override - public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, - @NonNull Context context) { - return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, - context); - } - - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return context.getResources().getBoolean(R.bool.config_showDialerRole); - } - - @Override public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) { if (SdkLevel.isAtLeastS()) { if (PackageUtils.isSystemPackage(packageName, context)) { diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DocumentManagerRoleBehavior.java index ce307fd82..c529c3daf 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/DocumentManagerRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.os.Process; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/EmergencyRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/EmergencyRoleBehavior.java index d0b2bf42a..7efa6242a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/EmergencyRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/EmergencyRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.PackageInfo; @@ -66,18 +66,4 @@ public class EmergencyRoleBehavior implements RoleBehavior { } return fallbackPackageInfo != null ? fallbackPackageInfo.packageName : null; } - - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return VisibilityMixin.isVisible("config_showDefaultEmergency", context); - } - - @Nullable - @Override - public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, - @NonNull Context context) { - return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, - context); - } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/HomeRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/HomeRoleBehavior.java index af6acfeec..9a476cd3b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/HomeRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/HomeRoleBehavior.java @@ -14,30 +14,20 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; -import android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings; -import android.app.role.RoleManager; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; -import android.os.Build; import android.os.UserHandle; import android.provider.Settings; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.Preference; -import com.android.permissioncontroller.R; -import com.android.permissioncontroller.permission.utils.CollectionUtils; -import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; import com.android.permissioncontroller.role.utils.UserUtils; import java.util.Arrays; @@ -53,8 +43,6 @@ import java.util.Objects; */ public class HomeRoleBehavior implements RoleBehavior { - private static final String LOG_TAG = HomeRoleBehavior.class.getSimpleName(); - private static final List<String> AUTOMOTIVE_PERMISSIONS = Arrays.asList( android.Manifest.permission.READ_CALL_LOG, android.Manifest.permission.WRITE_CALL_LOG, @@ -100,71 +88,10 @@ public class HomeRoleBehavior implements RoleBehavior { return packageName; } - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return VisibilityMixin.isVisible("config_showDefaultHome", context); - } - - @Override - public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference, - @NonNull UserHandle user, @NonNull Context context) { - TwoTargetPreference.OnSecondTargetClickListener listener = null; - RoleManager roleManager = context.getSystemService(RoleManager.class); - String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser( - role.getName(), user)); - if (packageName != null) { - Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES) - .setPackage(packageName) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - PackageManager userPackageManager = UserUtils.getUserContext(context, user) - .getPackageManager(); - ResolveInfo resolveInfo = userPackageManager.resolveActivity(intent, 0); - if (resolveInfo != null && resolveInfo.activityInfo != null - && resolveInfo.activityInfo.exported) { - listener = preference2 -> { - try { - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, "Cannot start activity for home app preferences", e); - } - }; - } - } - preference.setOnSecondTargetClickListener(listener); - } - - @Override - public boolean isApplicationVisibleAsUser(@NonNull Role role, - @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, - @NonNull Context context) { - // Home is not available for work profile, so we can just use the current user. - return !isSettingsApplication(applicationInfo, context); - } - - @Override - public void prepareApplicationPreferenceAsUser(@NonNull Role role, - @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, - @NonNull UserHandle user, @NonNull Context context) { - boolean missingWorkProfileSupport = isMissingWorkProfileSupport(applicationInfo, context); - preference.setEnabled(!missingWorkProfileSupport); - preference.setSummary(missingWorkProfileSupport ? Utils.getEnterpriseString(context, - DefaultAppSettings.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE, - R.string.home_missing_work_profile_support) : null); - } - - private boolean isMissingWorkProfileSupport(@NonNull ApplicationInfo applicationInfo, - @NonNull Context context) { - boolean hasWorkProfile = UserUtils.getWorkProfile(context) != null; - if (!hasWorkProfile) { - return false; - } - boolean isWorkProfileSupported = applicationInfo.targetSdkVersion - >= Build.VERSION_CODES.LOLLIPOP; - return !isWorkProfileSupported; - } - - private boolean isSettingsApplication(@NonNull ApplicationInfo applicationInfo, + /** + * Check if the application is a settings application + */ + public static boolean isSettingsApplication(@NonNull ApplicationInfo applicationInfo, @NonNull Context context) { PackageManager packageManager = context.getPackageManager(); ResolveInfo resolveInfo = packageManager.resolveActivity(new Intent( diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/IntentFilterData.java b/PermissionController/src/com/android/role/controller/model/IntentFilterData.java index 0bf1ce585..90deebfc6 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/IntentFilterData.java +++ b/PermissionController/src/com/android/role/controller/model/IntentFilterData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Intent; import android.content.IntentFilter; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permission.java b/PermissionController/src/com/android/role/controller/model/Permission.java index ae1b6d0e9..0c4a14574 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/Permission.java +++ b/PermissionController/src/com/android/role/controller/model/Permission.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.os.Build; @@ -60,9 +60,9 @@ public class Permission { * @return whether this permission is available */ public boolean isAvailable() { - // Workaround to match the value 33+ for T+ in roles.xml before SDK finalization. - if (mMinSdkVersion >= 33) { - return SdkLevel.isAtLeastT(); + // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization. + if (mMinSdkVersion >= 34) { + return SdkLevel.isAtLeastU(); } else { return Build.VERSION.SDK_INT >= mMinSdkVersion; } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/PermissionSet.java b/PermissionController/src/com/android/role/controller/model/PermissionSet.java index 1568c5c74..72f127e1c 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/PermissionSet.java +++ b/PermissionController/src/com/android/role/controller/model/PermissionSet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import androidx.annotation.NonNull; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java b/PermissionController/src/com/android/role/controller/model/Permissions.java index 28146a814..75a90b34e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java +++ b/PermissionController/src/com/android/role/controller/model/Permissions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.Manifest; import android.app.AppOpsManager; @@ -37,11 +37,11 @@ import androidx.annotation.Nullable; import com.android.permissioncontroller.permission.utils.ArrayUtils; import com.android.permissioncontroller.permission.utils.CollectionUtils; -import com.android.permissioncontroller.permission.utils.PermissionMapping; import com.android.permissioncontroller.permission.utils.Utils; import com.android.permissioncontroller.role.utils.PackageUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; @@ -57,6 +57,19 @@ public class Permissions { private static ArrayMap<String, String> sForegroundToBackgroundPermission; private static ArrayMap<String, List<String>> sBackgroundToForegroundPermissions; private static final Object sForegroundBackgroundPermissionMappingsLock = new Object(); + private static final List<String> SMS_PERMISSIONS = Arrays.asList( + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.READ_SMS, + Manifest.permission.RECEIVE_MMS, + Manifest.permission.RECEIVE_WAP_PUSH, + Manifest.permission.READ_CELL_BROADCASTS + ); + private static final List<String> CALL_LOG_PERMISSIONS = Arrays.asList( + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG, + Manifest.permission.PROCESS_OUTGOING_CALLS + ); /** * Filter a list of permissions based on their SDK versions. @@ -181,16 +194,12 @@ public class Permissions { Set<String> whitelistedRestrictedPermissions = new ArraySet<>( packageManager.getWhitelistedRestrictedPermissions(packageName, PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)); - List<String> smsPermissions = PermissionMapping.getPlatformPermissionNamesOfGroup( - Manifest.permission_group.SMS); - List<String> callLogPermissions = PermissionMapping.getPlatformPermissionNamesOfGroup( - Manifest.permission_group.CALL_LOG); int sortedPermissionsToGrantLength = sortedPermissionsToGrant.length; for (int i = 0; i < sortedPermissionsToGrantLength; i++) { String permission = sortedPermissionsToGrant[i]; - if ((smsPermissions.contains(permission) || callLogPermissions.contains(permission)) + if ((SMS_PERMISSIONS.contains(permission) || CALL_LOG_PERMISSIONS.contains(permission)) && whitelistedRestrictedPermissions.add(permission)) { packageManager.addWhitelistedRestrictedPermission(packageName, permission, PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM); diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/PreferredActivity.java b/PermissionController/src/com/android/role/controller/model/PreferredActivity.java index f03527b36..5b9c22b67 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/PreferredActivity.java +++ b/PermissionController/src/com/android/role/controller/model/PreferredActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredActivity.java b/PermissionController/src/com/android/role/controller/model/RequiredActivity.java index def0a6dbf..6b74dd4d8 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredActivity.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredBroadcastReceiver.java b/PermissionController/src/com/android/role/controller/model/RequiredBroadcastReceiver.java index 1f756f618..f191a1101 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredBroadcastReceiver.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredBroadcastReceiver.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredComponent.java b/PermissionController/src/com/android/role/controller/model/RequiredComponent.java index 361727d59..ced185b9d 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredComponent.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredComponent.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; @@ -101,9 +101,9 @@ public abstract class RequiredComponent { * @return whether this required component is available */ public boolean isAvailable() { - // Workaround to match the value 33+ for T+ in roles.xml before SDK finalization. - if (mMinTargetSdkVersion >= 33) { - return SdkLevel.isAtLeastT(); + // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization. + if (mMinTargetSdkVersion >= 34) { + return SdkLevel.isAtLeastU(); } else { return Build.VERSION.SDK_INT >= mMinTargetSdkVersion; } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredContentProvider.java b/PermissionController/src/com/android/role/controller/model/RequiredContentProvider.java index 76c82faf6..c24fa423c 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredContentProvider.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredContentProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredMetaData.java b/PermissionController/src/com/android/role/controller/model/RequiredMetaData.java index f7f701fc3..07b1a294d 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredMetaData.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredMetaData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.os.Bundle; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredService.java b/PermissionController/src/com/android/role/controller/model/RequiredService.java index 72d1439c9..3df7304e8 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredService.java +++ b/PermissionController/src/com/android/role/controller/model/RequiredService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.ComponentName; import android.content.Context; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java b/PermissionController/src/com/android/role/controller/model/Role.java index de6112e5e..0c3241d55 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java +++ b/PermissionController/src/com/android/role/controller/model/Role.java @@ -14,13 +14,12 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.app.ActivityManager; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; @@ -37,13 +36,11 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; -import androidx.preference.Preference; import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.Constants; import com.android.permissioncontroller.permission.utils.CollectionUtils; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; import com.android.permissioncontroller.role.utils.PackageUtils; import com.android.permissioncontroller.role.utils.RoleManagerCompat; import com.android.permissioncontroller.role.utils.UserUtils; @@ -124,6 +121,11 @@ public class Role { private final int mLabelResource; /** + * The maximum SDK version for this role to be available. + */ + private final int mMaxSdkVersion; + + /** * The minimum SDK version for this role to be available. */ private final int mMinSdkVersion; @@ -218,16 +220,21 @@ public class Role { @NonNull private final List<PreferredActivity> mPreferredActivities; + @Nullable + private final String mUiBehaviorName; + public Role(@NonNull String name, boolean allowBypassingQualification, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, - @StringRes int labelResource, int minSdkVersion, boolean overrideUserWhenGranting, - @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, - boolean requestable, @StringRes int searchKeywordsResource, - @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly, - boolean visible, @NonNull List<RequiredComponent> requiredComponents, + @StringRes int labelResource, int maxSdkVersion, int minSdkVersion, + boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource, + @StringRes int requestTitleResource, boolean requestable, + @StringRes int searchKeywordsResource, @StringRes int shortLabelResource, + boolean showNone, boolean statik, boolean systemOnly, boolean visible, + @NonNull List<RequiredComponent> requiredComponents, @NonNull List<Permission> permissions, @NonNull List<String> appOpPermissions, - @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) { + @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities, + @Nullable String uiBehaviorName) { mName = name; mAllowBypassingQualification = allowBypassingQualification; mBehavior = behavior; @@ -236,6 +243,7 @@ public class Role { mExclusive = exclusive; mFallBackToDefaultHolder = fallBackToDefaultHolder; mLabelResource = labelResource; + mMaxSdkVersion = maxSdkVersion; mMinSdkVersion = minSdkVersion; mOverrideUserWhenGranting = overrideUserWhenGranting; mRequestDescriptionResource = requestDescriptionResource; @@ -252,6 +260,7 @@ public class Role { mAppOpPermissions = appOpPermissions; mAppOps = appOps; mPreferredActivities = preferredActivities; + mUiBehaviorName = uiBehaviorName; } @NonNull @@ -345,6 +354,11 @@ public class Role { return mPreferredActivities; } + @Nullable + public String getUiBehaviorName() { + return mUiBehaviorName; + } + /** * Callback when this role is added to the system for the first time. * @@ -380,11 +394,12 @@ public class Role { * @return whether this role is available based on SDK version */ boolean isAvailableBySdkVersion() { - // Workaround to match the value 33+ for T+ in roles.xml before SDK finalization. - if (mMinSdkVersion >= 33) { - return SdkLevel.isAtLeastT(); + // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization. + if (mMinSdkVersion >= 34) { + return SdkLevel.isAtLeastU(); } else { - return Build.VERSION.SDK_INT >= mMinSdkVersion; + return Build.VERSION.SDK_INT >= mMinSdkVersion + && Build.VERSION.SDK_INT <= mMaxSdkVersion; } } @@ -526,110 +541,6 @@ public class Role { } /** - * Check whether this role should be visible to user. - * - * @param user the user to check for - * @param context the {@code Context} to retrieve system services - * - * @return whether this role should be visible to user - */ - public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) { - return mVisible && (mBehavior == null || mBehavior.isVisibleAsUser(this, user, context)); - } - - /** - * Check whether this role should be visible to user, for current user. - * - * @param context the {@code Context} to retrieve system services - * - * @return whether this role should be visible to user. - */ - public boolean isVisible(@NonNull Context context) { - return isVisibleAsUser(Process.myUserHandle(), context); - } - - /** - * Get the {@link Intent} to manage this role, or {@code null} to use the default UI. - * - * @param user the user to manage this role for - * @param context the {@code Context} to retrieve system services - * - * @return the {@link Intent} to manage this role, or {@code null} to use the default UI. - */ - @Nullable - public Intent getManageIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { - if (mBehavior != null) { - return mBehavior.getManageIntentAsUser(this, user, context); - } - return null; - } - - /** - * Prepare a {@link Preference} for this role. - * - * @param preference the {@link Preference} for this role - * @param user the user for this role - * @param context the {@code Context} to retrieve system services - */ - public void preparePreferenceAsUser(@NonNull TwoTargetPreference preference, - @NonNull UserHandle user, @NonNull Context context) { - if (mBehavior != null) { - mBehavior.preparePreferenceAsUser(this, preference, user, context); - } - } - - /** - * Check whether a qualifying application should be visible to user. - * - * @param applicationInfo the {@link ApplicationInfo} for the application - * @param user the user for the application - * @param context the {@code Context} to retrieve system services - * - * @return whether the qualifying application should be visible to user - */ - public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo, - @NonNull UserHandle user, @NonNull Context context) { - if (mBehavior != null) { - return mBehavior.isApplicationVisibleAsUser(this, applicationInfo, user, context); - } - return true; - } - - /** - * Prepare a {@link Preference} for an application. - * - * @param preference the {@link Preference} for the application - * @param applicationInfo the {@link ApplicationInfo} for the application - * @param user the user for the application - * @param context the {@code Context} to retrieve system services - */ - public void prepareApplicationPreferenceAsUser(@NonNull Preference preference, - @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, - @NonNull Context context) { - if (mBehavior != null) { - mBehavior.prepareApplicationPreferenceAsUser(this, preference, applicationInfo, user, - context); - } - } - - /** - * Get the confirmation message for adding an application as a holder of this role. - * - * @param packageName the package name of the application to get confirmation message for - * @param context the {@code Context} to retrieve system services - * - * @return the confirmation message, or {@code null} if no confirmation is needed - */ - @Nullable - public CharSequence getConfirmationMessage(@NonNull String packageName, - @NonNull Context context) { - if (mBehavior != null) { - return mBehavior.getConfirmationMessage(this, packageName, context); - } - return null; - } - - /** * Check whether this role is allowed to bypass qualification, if enabled globally. * * @param context the {@code Context} to retrieve system services @@ -1060,6 +971,7 @@ public class Role { + ", mExclusive=" + mExclusive + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder + ", mLabelResource=" + mLabelResource + + ", mMaxSdkVersion=" + mMaxSdkVersion + ", mMinSdkVersion=" + mMinSdkVersion + ", mOverrideUserWhenGranting=" + mOverrideUserWhenGranting + ", mRequestDescriptionResource=" + mRequestDescriptionResource @@ -1076,6 +988,7 @@ public class Role { + ", mAppOpPermissions=" + mAppOpPermissions + ", mAppOps=" + mAppOps + ", mPreferredActivities=" + mPreferredActivities + + ", mUiBehaviorName=" + mUiBehaviorName + '}'; } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RoleBehavior.java b/PermissionController/src/com/android/role/controller/model/RoleBehavior.java index e5402c2ed..f0c4fc018 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/RoleBehavior.java @@ -14,18 +14,13 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.os.UserHandle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.Preference; - -import com.android.permissioncontroller.role.ui.TwoTargetPreference; import java.util.Collections; import java.util.List; @@ -65,56 +60,6 @@ public interface RoleBehavior { } /** - * @see Role#isVisibleAsUser(UserHandle, Context) - */ - default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return true; - } - - /** - * @see Role#getManageIntentAsUser(UserHandle, Context) - */ - @Nullable - default Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return null; - } - - /** - * @see Role#preparePreferenceAsUser(TwoTargetPreference, UserHandle, Context) - */ - default void preparePreferenceAsUser(@NonNull Role role, - @NonNull TwoTargetPreference preference, @NonNull UserHandle user, - @NonNull Context context) {} - - /** - * @see Role#isApplicationVisibleAsUser(ApplicationInfo, UserHandle, Context) - */ - default boolean isApplicationVisibleAsUser(@NonNull Role role, - @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, - @NonNull Context context) { - return true; - } - - /** - * @see Role#prepareApplicationPreferenceAsUser(Preference, ApplicationInfo, UserHandle, - * Context) - */ - default void prepareApplicationPreferenceAsUser(@NonNull Role role, - @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, - @NonNull UserHandle user, @NonNull Context context) {} - - /** - * @see Role#getConfirmationMessage(String, Context) - */ - @Nullable - default CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, - @NonNull Context context) { - return null; - } - - /** * @see Role#shouldAllowBypassingQualification(Context) */ @Nullable diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java b/PermissionController/src/com/android/role/controller/model/RoleParser.java index 3d5e2e89c..09b12bb51 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java +++ b/PermissionController/src/com/android/role/controller/model/RoleParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.app.AppOpsManager; import android.content.Context; @@ -31,8 +31,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.permissioncontroller.R; - import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -41,6 +39,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Function; /** * Parser for {@link Role} definitions. @@ -48,6 +47,11 @@ import java.util.Objects; @VisibleForTesting public class RoleParser { + /** + * Function to retrieve the roles.xml resource from a context + */ + public static volatile Function<Context, XmlResourceParser> sGetRolesXml; + private static final String LOG_TAG = RoleParser.class.getSimpleName(); private static final String TAG_ROLES = "roles"; @@ -80,6 +84,7 @@ public class RoleParser { private static final String ATTRIBUTE_EXCLUSIVE = "exclusive"; private static final String ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER = "fallBackToDefaultHolder"; private static final String ATTRIBUTE_LABEL = "label"; + private static final String ATTRIBUTE_MAX_SDK_VERSION = "maxSdkVersion"; private static final String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; private static final String ATTRIBUTE_OVERRIDE_USER_WHEN_GRANTING = "overrideUserWhenGranting"; private static final String ATTRIBUTE_QUERY_FLAGS = "queryFlags"; @@ -91,6 +96,7 @@ public class RoleParser { private static final String ATTRIBUTE_SHOW_NONE = "showNone"; private static final String ATTRIBUTE_STATIC = "static"; private static final String ATTRIBUTE_SYSTEM_ONLY = "systemOnly"; + private static final String ATTRIBUTE_UI_BEHAVIOR = "uiBehavior"; private static final String ATTRIBUTE_VISIBLE = "visible"; private static final String ATTRIBUTE_MIN_TARGET_SDK_VERSION = "minTargetSdkVersion"; private static final String ATTRIBUTE_PERMISSION = "permission"; @@ -137,7 +143,7 @@ public class RoleParser { */ @NonNull public ArrayMap<String, Role> parse() { - try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.roles)) { + try (XmlResourceParser parser = sGetRolesXml.apply(mContext)) { Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser); if (xml == null) { return new ArrayMap<>(); @@ -350,8 +356,17 @@ public class RoleParser { boolean fallBackToDefaultHolder = getAttributeBooleanValue(parser, ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER, false); + int maxSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MAX_SDK_VERSION, + Build.VERSION_CODES.CUR_DEVELOPMENT); int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION, Build.VERSION_CODES.BASE); + if (minSdkVersion > maxSdkVersion) { + throwOrLogMessage("minSdkVersion " + minSdkVersion + + " cannot be greater than maxSdkVersion " + maxSdkVersion + " for role: " + + name); + skipCurrentTag(parser); + return null; + } boolean overrideUserWhenGranting = getAttributeBooleanValue(parser, ATTRIBUTE_OVERRIDE_USER_WHEN_GRANTING, false); @@ -398,6 +413,8 @@ public class RoleParser { boolean systemOnly = getAttributeBooleanValue(parser, ATTRIBUTE_SYSTEM_ONLY, false); + String uiBehaviorName = getAttributeValue(parser, ATTRIBUTE_UI_BEHAVIOR); + List<RequiredComponent> requiredComponents = null; List<Permission> permissions = null; List<String> appOpPermissions = null; @@ -478,10 +495,10 @@ public class RoleParser { } return new Role(name, allowBypassingQualification, behavior, defaultHoldersResourceName, descriptionResource, exclusive, fallBackToDefaultHolder, labelResource, - minSdkVersion, overrideUserWhenGranting, requestDescriptionResource, + maxSdkVersion, minSdkVersion, overrideUserWhenGranting, requestDescriptionResource, requestTitleResource, requestable, searchKeywordsResource, shortLabelResource, showNone, statik, systemOnly, visible, requiredComponents, permissions, - appOpPermissions, appOps, preferredActivities); + appOpPermissions, appOps, preferredActivities, uiBehaviorName); } @NonNull diff --git a/service/java/com/android/access/AccessCheckingService.kt b/PermissionController/src/com/android/role/controller/model/RoleParserInitializer.java index e61a245fd..81e338da9 100644 --- a/service/java/com/android/access/AccessCheckingService.kt +++ b/PermissionController/src/com/android/role/controller/model/RoleParserInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,11 +14,21 @@ * limitations under the License. */ -package com.android.access +package com.android.role.controller.model; -import androidx.annotation.Keep +import com.android.permissioncontroller.R; -@Keep -class AccessCheckingService { - var list = mutableListOf<Any>() +/** + * Initialize the function to retrieve the roles.xml resource from a context within + * PermissionController APK + */ +public class RoleParserInitializer { + + /** + * Initialize the function to retrieve the roles.xml resource from a context within + * PermissionController APK + */ + public static void initialize() { + RoleParser.sGetRolesXml = context -> context.getResources().getXml(R.xml.roles); + } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Roles.java b/PermissionController/src/com/android/role/controller/model/Roles.java index 6312b170e..5914ffbae 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/Roles.java +++ b/PermissionController/src/com/android/role/controller/model/Roles.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.util.ArrayMap; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SmsRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SmsRoleBehavior.java index 1515945c1..b144eab2f 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/SmsRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/SmsRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.os.Process; @@ -26,7 +26,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; -import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.CollectionUtils; import com.android.permissioncontroller.role.utils.PackageUtils; import com.android.permissioncontroller.role.utils.UserUtils; @@ -54,9 +53,16 @@ public class SmsRoleBehavior implements RoleBehavior { @Override public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { - if (UserUtils.isProfile(user, context)) { - return false; + if (SdkLevel.isAtLeastU()) { + if (UserUtils.isCloneProfile(user, context)) { + return false; + } + } else { + if (UserUtils.isProfile(user, context)) { + return false; + } } + UserManager userManager = context.getSystemService(UserManager.class); if (userManager.isRestrictedProfile(user)) { return false; @@ -86,20 +92,6 @@ public class SmsRoleBehavior implements RoleBehavior { return CollectionUtils.firstOrNull(qualifyingPackageNames); } - @Nullable - @Override - public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, - @NonNull Context context) { - return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName, - context); - } - - @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, - @NonNull Context context) { - return context.getResources().getBoolean(R.bool.config_showSmsRole); - } - @Override public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) { if (SdkLevel.isAtLeastS() && PackageUtils.isSystemPackage(packageName, context)) { diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SystemShellRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SystemShellRoleBehavior.java index 9476e2bae..548d30157 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/SystemShellRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/SystemShellRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.PackageManager; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SystemUiRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SystemUiRoleBehavior.java index c08232931..4d3912a40 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/SystemUiRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/SystemUiRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.PackageManager; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/TelevisionRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/TelevisionRoleBehavior.java index b854e3e35..e89b6fe34 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/TelevisionRoleBehavior.java +++ b/PermissionController/src/com/android/role/controller/model/TelevisionRoleBehavior.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.pm.PackageManager; diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/UserDeniedManager.java b/PermissionController/src/com/android/role/controller/model/UserDeniedManager.java index afe35f670..cc88f97af 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/model/UserDeniedManager.java +++ b/PermissionController/src/com/android/role/controller/model/UserDeniedManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.role.model; +package com.android.role.controller.model; import android.content.Context; import android.content.SharedPreferences; diff --git a/PermissionController/tests/inprocess/Android.bp b/PermissionController/tests/inprocess/Android.bp index 873e0704a..0cc355daa 100644 --- a/PermissionController/tests/inprocess/Android.bp +++ b/PermissionController/tests/inprocess/Android.bp @@ -44,11 +44,16 @@ android_test { "androidx.test.rules", "androidx.test.ext.truth", "androidx.test.ext.junit", - "androidx.test.uiautomator", + "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", "permission-test-util-lib", ], + data: [ + ":AppThatUsesCameraPermission", + ], + per_testcase_directory: true, + certificate: "platform", instrumentation_for: "PermissionController", diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp index 55e75401c..e6074e335 100644 --- a/PermissionController/tests/mocking/Android.bp +++ b/PermissionController/tests/mocking/Android.bp @@ -108,6 +108,8 @@ android_test { "modules-utils-build_system", "safety-center-resources-lib", "safety-center-internal-data", + "safety-label", + "lottie", "androidx.test.rules", "androidx.test.ext.truth", @@ -116,6 +118,11 @@ android_test { "mockito-target-extended-minus-junit4", ], + proto: { + type: "lite", + include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"], + }, + jni_libs: [ "libdexmakerjvmtiagent", "libstaticjvmtiagent", diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt index df6e92154..0f4216066 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt @@ -26,11 +26,11 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BACKGROUND -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BOTH -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_FOREGROUND -import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.SummaryMessage +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BACKGROUND +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BOTH +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_FOREGROUND +import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.SummaryMessage import com.android.permissioncontroller.permission.utils.Utils import com.android.settingslib.RestrictedLockUtils import org.junit.After @@ -42,10 +42,10 @@ import org.mockito.Mock import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import org.mockito.Mockito.spy +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.MockitoSession import org.mockito.quality.Strictness -import org.mockito.Mockito.`when` as whenever /** * Unit tests for [ReviewPermissionsViewModel] @@ -247,4 +247,4 @@ class ReviewPermissionsViewModelTest { requestedPermissionsFlags = listOf<Int>().toIntArray() } } -}
\ No newline at end of file +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt index 72c8716ec..b4b1abd96 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt @@ -38,14 +38,17 @@ import com.android.permissioncontroller.privacysources.PrivacySource import com.android.permissioncontroller.privacysources.SafetyCenterReceiver import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_DEVICE_REBOOTED import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers @@ -75,7 +78,7 @@ class SafetyCenterReceiverTest { val application = Mockito.mock(PermissionControllerApplication::class.java) } - private val testCoroutineDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher() + private val testCoroutineDispatcher = TestCoroutineDispatcher() @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager @@ -226,4 +229,4 @@ class SafetyCenterReceiverTest { verifyZeroInteractions(mockPrivacySource) verifyZeroInteractions(mockPrivacySource2) } -}
\ No newline at end of file +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt index b8bc2adf9..2e7fd5351 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt @@ -18,16 +18,26 @@ package com.android.permissioncontroller.tests.mocking.role.model import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.android.permissioncontroller.role.model.RoleParser +import com.android.role.controller.model.RoleParser +import com.android.role.controller.model.RoleParserInitializer +import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoleParserTest { + companion object { + @BeforeClass + @JvmStatic + fun setupBeforeClass() { + RoleParserInitializer.initialize() + } + } + private val targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext() @Test fun testParseRolesWithValidation() { RoleParser(targetContext, true).parse() } -}
\ No newline at end of file +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS new file mode 100644 index 000000000..5d8b8161b --- /dev/null +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 1026964 + +include /SafetyCenter/OWNERS diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt new file mode 100644 index 000000000..96fc5ee74 --- /dev/null +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt @@ -0,0 +1,267 @@ +/* + * 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.permissioncontroller.tests.mocking.safetycenter.ui.model + +import android.content.Context +import android.os.Build +import android.safetycenter.SafetyCenterData +import android.safetycenter.SafetyCenterIssue +import android.safetycenter.SafetyCenterStatus +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN +import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS +import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS +import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_NONE +import androidx.test.filters.SdkSuppress +import com.android.permissioncontroller.R +import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.TIRAMISU) +class StatusUiDataTest { + + @Mock private lateinit var mockContext: Context + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun copyForPendingActions_setsCorrectPendingActionsValue() { + val copiedWithPendingActions = + StatusUiData(STATUS, hasPendingActions = false).copyForPendingActions(true) + val copiedWithoutPendingActions = + StatusUiData(STATUS, hasPendingActions = true).copyForPendingActions(false) + + assertThat(copiedWithPendingActions.hasPendingActions).isTrue() + assertThat(copiedWithoutPendingActions.hasPendingActions).isFalse() + } + + @Test + fun getTitle_returnsTitle() { + assertThat(StatusUiData(STATUS).title).isEqualTo(STATUS.title) + assertThat(StatusUiData(ANOTHER_STATUS).title).isEqualTo(ANOTHER_STATUS.title) + assertThat(StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).title) + .isEqualTo(STATUS.title) + } + + @Test + fun getOriginalSummary_returnsOriginalSummary() { + assertThat(StatusUiData(STATUS).originalSummary).isEqualTo(STATUS.summary) + assertThat(StatusUiData(ANOTHER_STATUS).originalSummary).isEqualTo(ANOTHER_STATUS.summary) + assertThat( + StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())) + .originalSummary) + .isEqualTo(STATUS.summary) + } + + @Test + fun getSeverityLevel_returnsSeverityLevel() { + assertThat(StatusUiData(STATUS).severityLevel).isEqualTo(STATUS.severityLevel) + assertThat(StatusUiData(ANOTHER_STATUS).severityLevel) + .isEqualTo(ANOTHER_STATUS.severityLevel) + assertThat( + StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).severityLevel) + .isEqualTo(STATUS.severityLevel) + } + + @Test + fun getSummary_withoutPendingActions_returnsOriginalSummary() { + val dataWithoutPendingActions = StatusUiData(STATUS, hasPendingActions = false) + + val actualSummary = dataWithoutPendingActions.getSummary(mockContext) + + assertThat(actualSummary).isEqualTo(dataWithoutPendingActions.originalSummary) + } + + @Test + fun getSummary_withPendingActions_returnsQsSummary() { + val expectedSummary = "a quick settings summary" + whenever(mockContext.getString(R.string.safety_center_qs_status_summary)) + .thenReturn(expectedSummary) + + val actualSummary = StatusUiData(STATUS, hasPendingActions = true).getSummary(mockContext) + + assertThat(actualSummary).isEqualTo(expectedSummary) + } + + @Test + fun getContentDescription_returnsContentDescription() { + val expectedContentDescription = "a content description" + whenever( + mockContext.getString( + R.string.safety_status_preference_title_and_summary_content_description, + STATUS.title, + STATUS.summary)) + .thenReturn(expectedContentDescription) + + val actualContentDescription = StatusUiData(STATUS).getContentDescription(mockContext) + + assertThat(actualContentDescription).isEqualTo(expectedContentDescription) + } + + @Test + fun fromSafetyCenterData_withIssues_hasIssuesIsTrue() { + assertThat(StatusUiData(DATA_WITH_ISSUES).hasIssues).isTrue() + } + + @Test + fun fromSafetyCenterData_withoutIssues_hasIssuesIsFalse() { + assertThat(StatusUiData(DATA_WITHOUT_ISSUES).hasIssues).isFalse() + } + + @Test + fun hasPendingActions_defaultsFalse() { + assertThat(StatusUiData(STATUS).hasPendingActions).isFalse() + assertThat(StatusUiData(DATA_WITH_ISSUES).hasPendingActions).isFalse() + } + + @Test + fun getStatusImageResId_severityOk() { + assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_OK).statusImageResId) + .isEqualTo(R.drawable.safety_status_info) + } + + @Test + fun getStatusImageResId_severityUnknown() { + assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_UNKNOWN).statusImageResId) + .isEqualTo(R.drawable.safety_status_info) + } + @Test + fun getStatusImageResId_severityRecommendation() { + assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_RECOMMENDATION).statusImageResId) + .isEqualTo(R.drawable.safety_status_recommendation) + } + @Test + fun getStatusImageResId_severityWarning() { + assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING).statusImageResId) + .isEqualTo(R.drawable.safety_status_warn) + } + + @Test + fun isRefreshInProgress_dataFetch_isTrue() { + assertThat( + uiDataForRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS).isRefreshInProgress) + .isTrue() + } + + @Test + fun isRefreshInProgress_fullRescan_isTrue() { + assertThat( + uiDataForRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS).isRefreshInProgress) + .isTrue() + } + + @Test + fun isRefreshInProgress_none_isFalse() { + assertThat(uiDataForRefreshStatus(REFRESH_STATUS_NONE).isRefreshInProgress).isFalse() + } + + @Test + fun shouldShowRescanButton_severityOk_noIssues_noPendingActions_isTrue() { + assertThat( + StatusUiData( + statusForSeverity(OVERALL_SEVERITY_LEVEL_OK), + hasIssues = false, + hasPendingActions = false) + .shouldShowRescanButton()) + .isTrue() + } + + @Test + fun shouldShowRescanButton_severityUnknown_noIssues_noPendingActions_isTrue() { + assertThat( + StatusUiData( + statusForSeverity(OVERALL_SEVERITY_LEVEL_UNKNOWN), + hasIssues = false, + hasPendingActions = false) + .shouldShowRescanButton()) + .isTrue() + } + + @Test + fun shouldShowRescanButton_hasIssues_isFalse() { + assertThat( + StatusUiData( + statusForSeverity(OVERALL_SEVERITY_LEVEL_OK), + hasIssues = true, + hasPendingActions = false) + .shouldShowRescanButton()) + .isFalse() + } + + @Test + fun shouldShowRescanButton_hasPendingActions_isFalse() { + assertThat( + StatusUiData( + statusForSeverity(OVERALL_SEVERITY_LEVEL_OK), + hasIssues = false, + hasPendingActions = true) + .shouldShowRescanButton()) + .isFalse() + } + + @Test + fun shouldShowRescanButton_severityNotOkOrUnknown_isFalse() { + for (severity in + listOf( + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) { + assertThat( + StatusUiData( + statusForSeverity(severity), + hasIssues = false, + hasPendingActions = false) + .shouldShowRescanButton()) + .isFalse() + } + } + + private companion object { + val STATUS = + SafetyCenterStatus.Builder("a title", "a summary") + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) + .setRefreshStatus(REFRESH_STATUS_NONE) + .build() + + val ANOTHER_STATUS = + SafetyCenterStatus.Builder("another title", "another summary") + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) + .setRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS) + .build() + + val ISSUE = SafetyCenterIssue.Builder("iSsUe_Id", "issue title", "issue summary").build() + + val DATA_WITH_ISSUES = SafetyCenterData(STATUS, listOf(ISSUE), listOf(), listOf()) + val DATA_WITHOUT_ISSUES = SafetyCenterData(STATUS, listOf(), listOf(), listOf()) + + fun statusForSeverity(severityLevel: Int) = + SafetyCenterStatus.Builder(STATUS).setSeverityLevel(severityLevel).build() + + fun uiDataForSeverity(severityLevel: Int) = StatusUiData(statusForSeverity(severityLevel)) + + fun uiDataForRefreshStatus(refreshStatus: Int) = + StatusUiData(SafetyCenterStatus.Builder(STATUS).setRefreshStatus(refreshStatus).build()) + } +} diff --git a/PermissionController/tests/outofprocess/Android.bp b/PermissionController/tests/outofprocess/Android.bp index 806c8ecd7..e646def28 100644 --- a/PermissionController/tests/outofprocess/Android.bp +++ b/PermissionController/tests/outofprocess/Android.bp @@ -51,6 +51,7 @@ android_test { proto: { type: "lite", + include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"], }, test_suites: [ diff --git a/PermissionController/tests/permissionui/Android.bp b/PermissionController/tests/permissionui/Android.bp index 6d5c91043..704307c1f 100644 --- a/PermissionController/tests/permissionui/Android.bp +++ b/PermissionController/tests/permissionui/Android.bp @@ -45,25 +45,24 @@ android_test { "androidx.test.rules", "androidx.test.ext.truth", "androidx.test.ext.junit", - "androidx.test.uiautomator", + "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", "permission-test-util-lib", ], test_suites: [ "device-tests", + "general-tests", "mts-permission", ], - required: [ - "CtsAppThatRequestsLocationPermission29", - ], - data: [ + ":CtsAppThatRequestsLocationPermission29", ":PermissionUiUseStoragePermissionApp", ":PermissionUiUseCameraPermissionApp", ":PermissionUiDefineAdditionalPermissionApp", ":PermissionUiUseAdditionalPermissionApp", ":PermissionUiUseTwoAdditionalPermissionsApp", - ] + ], + per_testcase_directory: true, } diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml index 34a56286e..946f97e30 100644 --- a/PermissionController/tests/permissionui/AndroidTest.xml +++ b/PermissionController/tests/permissionui/AndroidTest.xml @@ -21,6 +21,7 @@ <option name="test-suite-tag" value="apct-instrumentation" /> <option name="test-tag" value="PermissionUiTestCases" /> <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" /> <!-- Install test --> <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> diff --git a/SafetyCenter/Config/Android.bp b/SafetyCenter/Config/Android.bp index 6615dd1c4..78480bf55 100644 --- a/SafetyCenter/Config/Android.bp +++ b/SafetyCenter/Config/Android.bp @@ -36,6 +36,7 @@ java_library { "androidx.annotation_annotation", "framework-annotations-lib", "framework-permission-s", + "modules-utils-build", ], apex_available: [ "com.android.permission", diff --git a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java index 8081ae472..f7faf9e27 100644 --- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java +++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java @@ -37,6 +37,8 @@ import android.safetycenter.config.SafetySourcesGroup; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; @@ -58,6 +60,7 @@ public final class SafetyCenterConfigParser { private static final String ATTR_SAFETY_SOURCES_GROUP_TITLE = "title"; private static final String ATTR_SAFETY_SOURCES_GROUP_SUMMARY = "summary"; private static final String ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE = "statelessIconType"; + private static final String ATTR_SAFETY_SOURCES_GROUP_TYPE = "type"; private static final String ATTR_SAFETY_SOURCE_ID = "id"; private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName"; private static final String ATTR_SAFETY_SOURCE_TITLE = "title"; @@ -71,8 +74,14 @@ public final class SafetyCenterConfigParser { private static final String ATTR_SAFETY_SOURCE_LOGGING_ALLOWED = "loggingAllowed"; private static final String ATTR_SAFETY_SOURCE_REFRESH_ON_PAGE_OPEN_ALLOWED = "refreshOnPageOpenAllowed"; + private static final String ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED = "notificationsAllowed"; + private static final String ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP = "deduplicationGroup"; + private static final String ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES = "packageCertificateHashes"; private static final String ENUM_STATELESS_ICON_TYPE_NONE = "none"; private static final String ENUM_STATELESS_ICON_TYPE_PRIVACY = "privacy"; + private static final String ENUM_GROUP_TYPE_STATEFUL = "stateful"; + private static final String ENUM_GROUP_TYPE_STATELESS = "stateless"; + private static final String ENUM_GROUP_TYPE_HIDDEN = "hidden"; private static final String ENUM_PROFILE_PRIMARY = "primary_profile_only"; private static final String ENUM_PROFILE_ALL = "all_profiles"; private static final String ENUM_INITIAL_DISPLAY_STATE_ENABLED = "enabled"; @@ -183,6 +192,18 @@ public final class SafetyCenterConfigParser { parser.getAttributeName(i), resources)); break; + case ATTR_SAFETY_SOURCES_GROUP_TYPE: + if (SdkLevel.isAtLeastU()) { + builder.setType( + parseGroupType( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources)); + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } + break; default: throw attributeUnexpected(name, parser.getAttributeName(i)); } @@ -321,6 +342,46 @@ public final class SafetyCenterConfigParser { parser.getAttributeName(i), resources)); break; + case ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED: + if (SdkLevel.isAtLeastU()) { + builder.setNotificationsAllowed( + parseBoolean( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources)); + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } + break; + case ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP: + if (SdkLevel.isAtLeastU()) { + builder.setDeduplicationGroup( + parseStringResourceValue( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources)); + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } + break; + case ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES: + if (SdkLevel.isAtLeastU()) { + String commaSeparatedHashes = + parseStringResourceValue( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources); + String[] splits = commaSeparatedHashes.split(","); + for (int j = 0; j < splits.length; j++) { + builder.addPackageCertificateHash(splits[j]); + } + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } + break; default: throw attributeUnexpected(name, parser.getAttributeName(i)); } @@ -501,6 +562,25 @@ public final class SafetyCenterConfigParser { } } + private static int parseGroupType( + @NonNull String valueString, + @NonNull String parent, + @NonNull String name, + @NonNull Resources resources) + throws ParseException { + String valueToParse = getValueToParse(valueString, parent, name, resources); + switch (valueToParse) { + case ENUM_GROUP_TYPE_STATEFUL: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL; + case ENUM_GROUP_TYPE_STATELESS: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS; + case ENUM_GROUP_TYPE_HIDDEN: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN; + default: + throw attributeInvalid(valueToParse, parent, name); + } + } + private static int parseProfile( @NonNull String valueString, @NonNull String parent, diff --git a/SafetyCenter/Config/tests/Android.bp b/SafetyCenter/Config/tests/Android.bp index cca539f6c..9d9975980 100644 --- a/SafetyCenter/Config/tests/Android.bp +++ b/SafetyCenter/Config/tests/Android.bp @@ -25,9 +25,10 @@ android_test { srcs: [ "java/**/*.kt", ], - required: [ - "SafetyCenterConfigTestsOverlay", + data: [ + ":SafetyCenterConfigTestsOverlay", ], + per_testcase_directory: true, static_libs: [ "compatibility-device-util-axt", "kotlinx-coroutines-android", diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt index 140da54d3..fc169fd5e 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt @@ -18,6 +18,7 @@ package com.android.safetycenter.config import android.content.Context import androidx.test.core.app.ApplicationProvider.getApplicationContext +import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.config.tests.R import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows @@ -33,7 +34,8 @@ class ParserConfigInvalidTest { private val testName: String, val configResourceId: Int, val errorMessage: String, - val causeErrorMessage: String? + val causeErrorMessage: String?, + val includeCondition: Boolean = true ) { override fun toString() = testName } @@ -237,6 +239,20 @@ class ParserConfigInvalidTest { "Element safety-sources-group invalid", "Safety sources group empty"), Params( + "ConfigSafetySourcesGroupHiddenWithOnlyDynamic", + R.raw.config_safety_sources_group_hidden_with_only_dynamic, + "Element safety-sources-group invalid", + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only", + SdkLevel.isAtLeastU()), + Params( + "ConfigSafetySourcesGroupHiddenWithOnlyStatic", + R.raw.config_safety_sources_group_hidden_with_only_static, + "Element safety-sources-group invalid", + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only", + SdkLevel.isAtLeastU()), + Params( "ConfigSafetySourcesGroupInvalidIcon", R.raw.config_safety_sources_group_invalid_icon, "Attribute value \"invalid\" in safety-sources-group.statelessIconType invalid", @@ -252,6 +268,20 @@ class ParserConfigInvalidTest { "Element safety-sources-group invalid", "Required attribute title missing"), Params( + "ConfigSafetySourcesGroupStatefulWithOnlyIssueOnly", + R.raw.config_safety_sources_group_stateful_with_only_issue_only, + "Element safety-sources-group invalid", + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden", + SdkLevel.isAtLeastU()), + Params( + "ConfigSafetySourcesGroupStatelessWithOnlyIssueOnly", + R.raw.config_safety_sources_group_stateless_with_only_issue_only, + "Element safety-sources-group invalid", + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceDuplicateKey", R.raw.config_static_safety_source_duplicate_key, "Element safety-sources-config invalid", @@ -282,6 +312,12 @@ class ParserConfigInvalidTest { "Element static-safety-source invalid", "Required attribute title missing"), Params( + "ConfigStaticSafetySourceWithDeduplicationGroups", + R.raw.config_static_safety_source_with_deduplication_groups, + "Element static-safety-source invalid", + "Prohibited attribute deduplicationGroup present", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceWithDisplay", R.raw.config_static_safety_source_with_display, "Element static-safety-source invalid", @@ -292,10 +328,23 @@ class ParserConfigInvalidTest { "Element static-safety-source invalid", "Prohibited attribute loggingAllowed present"), Params( + "ConfigStaticSafetySourceWithNotifications", + R.raw.config_static_safety_source_with_notifications, + "Element static-safety-source invalid", + "Prohibited attribute notificationsAllowed present", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceWithPackage", R.raw.config_static_safety_source_with_package, "Element static-safety-source invalid", - "Prohibited attribute packageName present"), + "Prohibited attribute packageName present", + !SdkLevel.isAtLeastU()), + Params( + "ConfigStaticSafetySourceWithPackageCertficates", + R.raw.config_static_safety_source_with_package_certs, + "Element static-safety-source invalid", + "Prohibited attribute packageCertificateHashes present", + SdkLevel.isAtLeastU()), Params( "ConfigStaticSafetySourceWithPrimaryAndWork", R.raw.config_static_safety_source_with_primary_and_work, @@ -347,5 +396,6 @@ class ParserConfigInvalidTest { "Resource name \"@com.android.safetycenter.config.tests:string/missing\" in " + "safety-sources-group.title missing or invalid", null)) + .filter { it.includeCondition } } } diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt index a8be2cdf0..573725582 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt @@ -22,6 +22,7 @@ import android.safetycenter.config.SafetySource import android.safetycenter.config.SafetySourcesGroup import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.config.tests.R import com.google.common.truth.Truth.assertThat import org.junit.Test @@ -68,6 +69,14 @@ class ParserConfigValidTest { .setSearchTermsResId(R.string.reference) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash("feed1") + addPackageCertificateHash("feed2") + } + } .build()) .addSafetySource( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -83,6 +92,14 @@ class ParserConfigValidTest { .setSearchTermsResId(R.string.reference) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash("feed1") + addPackageCertificateHash("feed2") + } + } .build()) .addSafetySource( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -127,6 +144,7 @@ class ParserConfigValidTest { .addSafetySource( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) .setId("static_all_optional") + .apply { if (SdkLevel.isAtLeastU()) setPackageName("package") } .setTitleResId(R.string.reference) .setTitleForWorkResId(R.string.reference) .setSummaryResId(R.string.reference) @@ -152,6 +170,14 @@ class ParserConfigValidTest { .setMaxSeverityLevel(300) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash("feed1") + addPackageCertificateHash("feed2") + } + } .build()) .build()) .addSafetySourcesGroup( @@ -181,6 +207,94 @@ class ParserConfigValidTest { .setProfile(SafetySource.PROFILE_PRIMARY) .build()) .build()) + .apply { + if (SdkLevel.isAtLeastU()) { + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + .setId("stateful_barebone") + .setTitleResId(R.string.reference) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("stateful_barebone_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + .setId("stateful_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("stateful_all_optional_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + .setId("stateless_barebone") + .setTitleResId(R.string.reference) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("stateless_barebone_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + .setId("stateless_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("stateless_all_optional_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId("hidden_barebone") + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) + .setId("hidden_barebone_source") + .setPackageName("package") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId("hidden_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) + .setId("hidden_all_optional_source") + .setPackageName("package") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + } + } .build() assertThat(actual).isEqualTo(expected) } diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml new file mode 100644 index 000000000..4be2c75ff --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml @@ -0,0 +1,15 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="hidden" + id="id"> + <dynamic-safety-source + id="id" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml new file mode 100644 index 000000000..65f9f0ac9 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml @@ -0,0 +1,14 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="hidden" + id="id"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateful_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateful_with_only_issue_only.xml new file mode 100644 index 000000000..f801ca544 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateful_with_only_issue_only.xml @@ -0,0 +1,13 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="stateful" + id="id" + title="@com.android.safetycenter.config.tests:string/reference"> + <issue-only-safety-source + id="id" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml new file mode 100644 index 000000000..9c05e59dd --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml @@ -0,0 +1,13 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="stateless" + id="id" + title="@com.android.safetycenter.config.tests:string/reference"> + <issue-only-safety-source + id="id" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml new file mode 100644 index 000000000..b75969ae1 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml @@ -0,0 +1,32 @@ +<!-- + ~ 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. + --> + +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only" + deduplicationGroup="group"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config>
\ No newline at end of file diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.xml new file mode 100644 index 000000000..920f5c44b --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.xml @@ -0,0 +1,16 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only" + notificationsAllowed="true"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml new file mode 100644 index 000000000..2a99d5617 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml @@ -0,0 +1,32 @@ +<!-- + ~ 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. + --> + +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only" + packageCertificateHashes="feed1,feed2"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml new file mode 100644 index 000000000..f9e7fbf27 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml @@ -0,0 +1,189 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="dynamic" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <dynamic-safety-source + id="dynamic_barebone" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <dynamic-safety-source + id="dynamic_all_optional" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + initialDisplayState="disabled" + maxSeverityLevel="300" + searchTerms="@com.android.safetycenter.config.tests:string/reference" + loggingAllowed="false" + refreshOnPageOpenAllowed="true" + notificationsAllowed="true" + deduplicationGroup="group" + packageCertificateHashes="feed1,feed2"/> + <dynamic-safety-source + id="@com.android.safetycenter.config.tests:string/dynamic_all_references_id" + packageName="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_name" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="@com.android.safetycenter.config.tests:string/dynamic_all_references_intent_action" + profile="@com.android.safetycenter.config.tests:string/dynamic_all_references_profile" + initialDisplayState="@com.android.safetycenter.config.tests:string/dynamic_all_references_initial_display_state" + maxSeverityLevel="@com.android.safetycenter.config.tests:string/dynamic_all_references_max_severity_level" + searchTerms="@com.android.safetycenter.config.tests:string/reference" + loggingAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_logging_allowed" + refreshOnPageOpenAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_refresh_on_page_open_allowed" + notificationsAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_notifications_allowed" + deduplicationGroup="@com.android.safetycenter.config.tests:string/dynamic_all_references_deduplication_group" + packageCertificateHashes="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_cert_hashes"/> + <dynamic-safety-source + id="dynamic_disabled" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + profile="primary_profile_only" + initialDisplayState="disabled"/> + <dynamic-safety-source + id="dynamic_hidden" + packageName="package" + profile="all_profiles" + initialDisplayState="hidden"/> + <dynamic-safety-source + id="dynamic_hidden_with_search" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + initialDisplayState="hidden" + searchTerms="@com.android.safetycenter.config.tests:string/reference"/> + </safety-sources-group> + <safety-sources-group + id="static" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="static_barebone" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <static-safety-source + id="static_all_optional" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + searchTerms="@com.android.safetycenter.config.tests:string/reference"/> + </safety-sources-group> + <safety-sources-group + id="issue_only"> + <issue-only-safety-source + id="issue_only_barebone" + packageName="package" + profile="primary_profile_only"/> + <issue-only-safety-source + id="issue_only_all_optional" + packageName="package" + profile="all_profiles" + maxSeverityLevel="300" + loggingAllowed="false" + refreshOnPageOpenAllowed="true" + notificationsAllowed="true" + deduplicationGroup="group" + packageCertificateHashes="feed1,feed2"/> + </safety-sources-group> + <safety-sources-group + id="mixed" + title="@com.android.safetycenter.config.tests:string/reference"> + <dynamic-safety-source + id="mixed_dynamic_barebone" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <issue-only-safety-source + id="mixed_issue_only_barebone" + packageName="package" + profile="primary_profile_only"/> + <static-safety-source + id="mixed_static_barebone" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateful" + id="stateful_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="stateful_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateful" + id="stateful_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="stateful_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateless" + id="stateless_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="stateless_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateless" + id="stateless_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="stateless_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_barebone"> + <issue-only-safety-source + id="hidden_barebone_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <issue-only-safety-source + id="hidden_all_optional_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/values/strings.xml b/SafetyCenter/Config/tests/res/values/strings.xml index 195f56c2a..a625c5225 100644 --- a/SafetyCenter/Config/tests/res/values/strings.xml +++ b/SafetyCenter/Config/tests/res/values/strings.xml @@ -26,4 +26,7 @@ <string name="dynamic_all_references_max_severity_level" translatable="false">300</string> <string name="dynamic_all_references_logging_allowed" translatable="false">false</string> <string name="dynamic_all_references_refresh_on_page_open_allowed" translatable="false">true</string> + <string name="dynamic_all_references_notifications_allowed" translatable="false">true</string> + <string name="dynamic_all_references_deduplication_group" translatable="false">group</string> + <string name="dynamic_all_references_package_cert_hashes" translatable="false">feed1,feed2</string> </resources> diff --git a/SafetyCenter/ConfigLintChecker/Android.bp b/SafetyCenter/ConfigLintChecker/Android.bp index fb2e7ce3e..08881ccd5 100644 --- a/SafetyCenter/ConfigLintChecker/Android.bp +++ b/SafetyCenter/ConfigLintChecker/Android.bp @@ -33,8 +33,9 @@ java_library_host { "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType "lint_api", ], - java_resources: [":safetycenter-config-schema"], + java_resources: [":safetycenter-config-schemas"], jarjar_rules: "jarjar-rules.txt", + kotlincflags: ["-Xjvm-default=all"], visibility: [ "//packages/modules/Permission:__subpackages__", "//vendor:__subpackages__", @@ -43,6 +44,12 @@ java_library_host { java_test_host { name: "ConfigLintCheckerTest", + // TODO(b/239881504): Since this test was written, Android + // Lint was updated, and now includes classes that were + // compiled for java 15. The soong build doesn't support + // java 15 yet, so we can't compile against "lint". Disable + // the test until java 15 is supported. + enabled: false, srcs: [ "tests/java/**/*.kt", ], diff --git a/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java b/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java new file mode 100644 index 000000000..c6d6dc757 --- /dev/null +++ b/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Indicates that Lint should ignore the specified warnings for the annotated element. */ +@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) +@Retention(RetentionPolicy.CLASS) +public @interface SuppressLint { + /** + * The set of warnings (identified by the lint issue id) that should be ignored by lint. It is + * not an error to specify an unrecognized name. + */ + String[] value(); +} diff --git a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java index abe5d49d3..531b6e481 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java +++ b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java @@ -20,9 +20,11 @@ package android.os; public final class Build { private Build() {} - /** Stub class to used in the Safety Center config files. */ + /** Stub class used in the Safety Center config code. */ public static final class VERSION_CODES { - /** Constant used in the Safety Center config files. */ - public static final int TIRAMISU = 10000; + /** Constant used in the Safety Center config code. */ + public static final int TIRAMISU = 33; + /** Constant used in the Safety Center config code. */ + public static final int UPSIDE_DOWN_CAKE = 34; } } diff --git a/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java b/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java index e1300de32..81bedc860 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java +++ b/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java @@ -29,6 +29,8 @@ public interface Parcel { int readInt(); /** Method used in the Safety Center config data structures. */ String readString(); + /** Method used in the Safety Center config data structures. */ + ArrayList<String> createStringArrayList(); /** Method used in the Safety Center config data structures. */ void writeBoolean(boolean value); @@ -37,5 +39,7 @@ public interface Parcel { /** Method used in the Safety Center config data structures. */ void writeString(String value); /** Method used in the Safety Center config data structures. */ + void writeStringList(List<String> value); + /** Method used in the Safety Center config data structures. */ <T extends Parcelable> void writeTypedList(List<T> value); } diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt index ed58e5341..0b00805c2 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt +++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt @@ -16,6 +16,7 @@ package android.safetycenter.lint +import android.os.Build.VERSION_CODES.TIRAMISU import com.android.resources.ResourceFolderType import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Context @@ -36,20 +37,19 @@ import org.xml.sax.SAXException class ConfigSchemaDetector : Detector(), OtherFileScanner { companion object { - val ISSUE = Issue.create( - id = "InvalidSafetyCenterConfigSchema", - briefDescription = "The Safety Center config does not meet the schema requirements", - explanation = """The Safety Center config must follow all constraints defined in \ + val ISSUE = + Issue.create( + id = "InvalidSafetyCenterConfigSchema", + briefDescription = "The Safety Center config does not meet the schema requirements", + explanation = + """The Safety Center config must follow all constraints defined in \ safety_center_config.xsd. Either the config is invalid or the schema is not up to date.""", - category = Category.CORRECTNESS, - severity = Severity.ERROR, - implementation = Implementation( - ConfigSchemaDetector::class.java, - Scope.OTHER_SCOPE - ), - androidSpecific = true - ) + category = Category.CORRECTNESS, + severity = Severity.ERROR, + implementation = + Implementation(ConfigSchemaDetector::class.java, Scope.OTHER_SCOPE), + androidSpecific = true) } override fun appliesTo(folderType: ResourceFolderType): Boolean { @@ -60,9 +60,31 @@ class ConfigSchemaDetector : Detector(), OtherFileScanner { if (context.file.name != "safety_center_config.xml") { return } - val xsd = StreamSource( - ConfigSchemaDetector::class.java.getResourceAsStream("/safety_center_config.xsd") - ) + val fileSdk = FileSdk.getSdkQualifier(context.file) + // A config must comply with the schema at the highest SDK level that is lower or equal to + // the SDK level of the config itself. + var found = false + for (sdk in fileSdk downTo TIRAMISU) { + if (testSchema(sdk, context)) { + found = true + break + } + } + if (!found) { + context.report( + ISSUE, + Location.create(context.file), + "No schema found for SDK level: $fileSdk, was it deleted?") + } + // Test new schemas for backward compatibility. + for (sdk in fileSdk + 1..FileSdk.getMaxSdkVersion()) { + testSchema(sdk, context) + } + } + + private fun testSchema(sdk: Int, context: Context): Boolean { + val xsdInputStream = FileSdk.getSchemaAsStream(sdk) ?: return false + val xsd = StreamSource(xsdInputStream) val xml = StreamSource(context.file.inputStream()) val schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) try { @@ -73,14 +95,13 @@ class ConfigSchemaDetector : Detector(), OtherFileScanner { context.report( ISSUE, Location.create(context.file), - e.message!! - ) + "SAXException exception at sdk=$sdk: \"${e.message}\"") } catch (e: IOException) { context.report( ISSUE, Location.create(context.file), - e.message!! - ) + "IOException exception at sdk=$sdk: \"${e.message}\"") } + return true } -}
\ No newline at end of file +} diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt new file mode 100644 index 000000000..f93d4ecab --- /dev/null +++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.safetycenter.lint + +import android.os.Build +import android.os.Build.VERSION_CODES.TIRAMISU +import com.google.common.annotations.VisibleForTesting +import java.io.File +import java.io.InputStream +import java.lang.reflect.Modifier.isFinal +import java.lang.reflect.Modifier.isPublic +import java.lang.reflect.Modifier.isStatic + +/** A class that allows interacting with files that are versioned by sdk. */ +object FileSdk { + /** + * Linter constant to limit the mocked SDK levels that will be checked. We are making an + * important assumption here that if new parser logic is introduced that depends on a new SDK + * level, we expect a new schema to exist and a new version code to have been added. + */ + private val MAX_VERSION: Int = maxOf(getMaxVersionCodesConstant(), getMaxSchemaVersion()) + + /** Test only override to further limit the mocked SDK that will be checked in a test */ + @VisibleForTesting @Volatile @JvmStatic var maxVersionOverride: Int? = null + + /** + * Returns the max SDK level version that should be used while linting to check for backward + * compatibility. + */ + fun getMaxSdkVersion(): Int = maxVersionOverride ?: MAX_VERSION + + /** Returns the SDK level version that a file resource belongs to. */ + fun getSdkQualifier(file: File): Int { + val directParentName = file.parentFile.name + val lastQualifier = directParentName.substringAfterLast("-", "") + if (lastQualifier.isEmpty() || lastQualifier[0] != 'v') { + return TIRAMISU + } + return try { + lastQualifier.substring(1).toInt() + } catch (nfe: NumberFormatException) { + TIRAMISU + } + } + + /** Returns the schema for the specific SDK level provided or null if it doesn't exist. */ + fun getSchemaAsStream(sdk: Int): InputStream? = + FileSdk::class.java.getResourceAsStream("/safety_center_config${toQualifier(sdk)}.xsd") + + private fun toQualifier(sdk: Int): String = if (sdk == TIRAMISU) "" else "-v$sdk" + + private fun getMaxVersionCodesConstant(): Int = + Build.VERSION_CODES::class + .java + .declaredFields + .filter { + isPublic(it.modifiers) && + isFinal(it.modifiers) && + isStatic(it.modifiers) && + it.type == Integer.TYPE + } + .maxOf { it.get(null) as Int } + + private fun getMaxSchemaVersion(): Int = + // 99 is an arbitrary high value to look for the schema with the highest SDK level. + // Gaps are possible which is why we cannot just stop as soon as an SDK level has no schema. + (TIRAMISU..99).filter { getSchemaAsStream(it) != null }.maxOrNull() ?: 0 +} diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt index e512b7e54..09f074818 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt +++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt @@ -19,6 +19,7 @@ package android.safetycenter.lint import android.content.res.Resources import com.android.SdkConstants.ATTR_NAME import com.android.SdkConstants.TAG_STRING +import com.android.modules.utils.build.SdkLevel import com.android.resources.ResourceFolderType import com.android.safetycenter.config.ParseException import com.android.safetycenter.config.SafetyCenterConfigParser @@ -103,21 +104,34 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { context.file.name != "safety_center_config.xml") { return } - try { - SafetyCenterConfigParser.parseXmlResource( - context.file.inputStream(), - // Note: using a map of the string resources present in the APK under analysis is - // necessary in order to get the value of string resources that are resolved and - // validated at parse time. The drawback of this is that the linter cannot be used - // on overlay packages that refer to resources in the target package or on packages - // that refer to Android global resources. However, we cannot use custom a linter - // with the default soong overlay build rule regardless. - Resources(context.project.`package`, mNameToIndex, mIndexToValue)) - } catch (e: ParseException) { - context.report( - ISSUE, - Location.create(context.file), - "Parser exception: \"${e.message}\", cause: \"${e.cause?.message}\"") + val minSdk = FileSdk.getSdkQualifier(context.file) + val maxSdk = maxOf(minSdk, FileSdk.getMaxSdkVersion()) + // Test the parser at the SDK level for which the config was designed. + // Then test parsers at higher SDK levels for backward compatibility. + // This is slightly inefficient if a parser at a higher SDK level has no behavioral changes + // compared to one at a lower SDK level, but doing an exhaustive search is safer. + for (sdk in minSdk..maxSdk) { + synchronized(SdkLevel::class.java) { + SdkLevel.setSdkInt(sdk) + try { + SafetyCenterConfigParser.parseXmlResource( + context.file.inputStream(), + // Note: using a map of the string resources present in the APK under + // analysis is necessary in order to get the value of string resources that + // are resolved and validated at parse time. The drawback of this is that + // the linter cannot be used on overlay packages that refer to resources in + // the target package or on packages that refer to Android global resources. + // However, we cannot use a custom linter with the default soong overlay + // build rule regardless. + Resources(context.project.`package`, mNameToIndex, mIndexToValue)) + } catch (e: ParseException) { + context.report( + ISSUE, + Location.create(context.file), + "Parser exception at sdk=$sdk: \"${e.message}\", cause: " + + "\"${e.cause?.message}\"") + } + } } } } diff --git a/service/java/com/android/access/package-info.java b/SafetyCenter/ConfigLintChecker/java/android/util/ArraySet.java index 18fcfc69d..cfc864c65 100644 --- a/service/java/com/android/access/package-info.java +++ b/SafetyCenter/ConfigLintChecker/java/android/util/ArraySet.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,9 +14,15 @@ * limitations under the License. */ +package android.util; + +import java.util.HashSet; + /** - * @hide - * TODO(b/146466118) remove this javadoc tag + * A simple ArraySet implementation for the lint checker. + * + * <p>It's not array based, but for this simple purpose that doesn't matter. + * + * @param <E> the type of elements maintained by this set */ -@android.annotation.Hide -package com.android.access; +public final class ArraySet<E> extends HashSet<E> {} diff --git a/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java new file mode 100644 index 000000000..dbfaa56b1 --- /dev/null +++ b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java @@ -0,0 +1,45 @@ +/* + * 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.modules.utils.build; + +import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; + +/** Stub class to compile the linter for host execution. */ +public final class SdkLevel { + private SdkLevel() {} + + private static volatile int sSdkInt = TIRAMISU; + + /** + * Linter only method to set the mocked SDK level for the Safety Center config code. + * + * <p>You must hold the class lock before calling this method. You should hold the class lock + * for the whole duration of the lint check. + */ + public static void setSdkInt(int sdkInt) { + if (!Thread.holdsLock(SdkLevel.class)) { + throw new IllegalStateException("Lock not held."); + } + sSdkInt = sdkInt; + } + + /** Method used in the Safety Center config code. */ + public static boolean isAtLeastU() { + return sSdkInt >= UPSIDE_DOWN_CAKE; + } +} diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt index 0dd4afbd2..754d1a71f 100644 --- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt +++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt @@ -16,11 +16,14 @@ package android.safetycenter.lint.test +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.lint.ConfigSchemaDetector +import android.safetycenter.lint.FileSdk import com.android.tools.lint.checks.infrastructure.LintDetectorTest import com.android.tools.lint.checks.infrastructure.TestLintTask import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Issue +import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -34,11 +37,45 @@ class ConfigSchemaDetectorTest : LintDetectorTest() { override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + @After + fun resetOverrides() { + FileSdk.maxVersionOverride = null + } + + @Test + fun validMinimumConfig_doesNotThrow() { + lint() + .files( + (xml( + "res/raw/safety_center_config.xml", + """ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="group" + title="@package:string/reference" + summary="@package:string/reference"> + <static-safety-source + id="source" + title="@package:string/reference" + summary="@package:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> + """))) + .run() + .expectClean() + } + @Test - fun validConfig_doesNotThrow() { - lint().files(( - xml("res/raw/safety_center_config.xml", - """ + fun validFutureConfig_doesNotThrow() { + lint() + .files( + (xml( + "res/raw-99/safety_center_config.xml", + """ <safety-center-config> <safety-sources-config> <safety-sources-group @@ -54,26 +91,82 @@ class ConfigSchemaDetectorTest : LintDetectorTest() { </safety-sources-group> </safety-sources-config> </safety-center-config> - """))).run().expectClean() + """))) + .run() + .expectClean() } @Test - fun invalidConfig_throws() { - lint().files((xml("res/raw/safety_center_config.xml", "<invalid-root/>"))) - .run().expect("res/raw/safety_center_config.xml: Error: cvc-elt.1.a: Cannot find the " + - "declaration of element 'invalid-root'. [InvalidSafetyCenterConfigSchema]\n1 " + - "errors, 0 warnings") + fun invalidConfigWithSingleSchema_throwsOneError() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE + lint() + .files((xml("res/raw-v34/safety_center_config.xml", "<invalid-root/>"))) + .run() + .expect( + "res/raw-v34/safety_center_config.xml: Error: SAXException exception at sdk=34: " + + "\"cvc-elt.1.a: Cannot find the declaration of element 'invalid-root'.\" " + + "[InvalidSafetyCenterConfigSchema]\n1 errors, 0 warnings") + } + + @Test + fun invalidConfigWithMultipleSchemas_throwsMultipleErrors() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE + lint() + .files((xml("res/raw/safety_center_config.xml", "<invalid-root/>"))) + .run() + .expect( + "res/raw/safety_center_config.xml: Error: SAXException exception at sdk=33: " + + "\"cvc-elt.1.a: Cannot find the declaration of element 'invalid-root'.\" " + + "[InvalidSafetyCenterConfigSchema]\nres/raw/safety_center_config.xml: " + + "Error: SAXException exception at sdk=34: \"cvc-elt.1.a: Cannot find the " + + "declaration of element 'invalid-root'.\" " + + "[InvalidSafetyCenterConfigSchema]\n2 errors, 0 warnings") + } + + @Test + fun validUConfigWithUFields_throwsInT() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE + lint() + .files( + (xml( + "res/raw/safety_center_config.xml", + """ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="group" + title="@package:string/reference" + summary="@package:string/reference"> + <dynamic-safety-source + id="source" + packageName="package" + title="@package:string/reference" + summary="@package:string/reference" + intentAction="intent" + profile="primary_profile_only" + notificationsAllowed="true"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> + """))) + .run() + .expect( + "res/raw/safety_center_config.xml: Error: SAXException exception at sdk=33: " + + "\"cvc-complex-type.3.2.2: Attribute 'notificationsAllowed' is not allowed " + + "to appear in element 'dynamic-safety-source'.\" " + + "[InvalidSafetyCenterConfigSchema]\n1 errors, 0 warnings") } @Test fun unrelatedFile_doesNotThrow() { - lint().files((xml("res/raw/some_other_config.xml", "<some-other-root/>"))) - .run().expectClean() + lint() + .files((xml("res/raw/some_other_config.xml", "<some-other-root/>"))) + .run() + .expectClean() } @Test fun unrelatedFolder_doesNotThrow() { - lint().files((xml("res/values/strings.xml", "<some-other-root/>"))) - .run().expectClean() + lint().files((xml("res/values/strings.xml", "<some-other-root/>"))).run().expectClean() } } diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt index ad7d36685..a3030554e 100644 --- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt +++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt @@ -16,11 +16,15 @@ package android.safetycenter.lint.test +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.safetycenter.lint.FileSdk import android.safetycenter.lint.ParserExceptionDetector import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile import com.android.tools.lint.checks.infrastructure.TestLintTask import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Issue +import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -34,61 +38,130 @@ class ParserExceptionDetectorTest : LintDetectorTest() { override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + @After + fun resetOverrides() { + FileSdk.maxVersionOverride = null + } + @Test - fun validConfig_doesNotThrow() { + fun validMinimumConfig_doesNotThrow() { lint() .files( - (xml( - "res/raw/safety_center_config.xml", - """ -<safety-center-config> - <safety-sources-config> - <safety-sources-group - id="group" - title="@lint.test.pkg:string/reference" - summary="@lint.test.pkg:string/reference"> - <static-safety-source - id="source" - title="@lint.test.pkg:string/reference" - summary="@lint.test.pkg:string/reference" - intentAction="intent" - profile="primary_profile_only"/> - </safety-sources-group> - </safety-sources-config> -</safety-center-config> - """)), - (xml( - "res/values/strings.xml", - """ -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="reference" translatable="false">Reference</string> -</resources> - """))) + xml("res/raw/safety_center_config.xml", VALID_TIRAMISU_CONFIG), + STRINGS_WITH_REFERENCE_XML) .run() .expectClean() } @Test - fun invalidConfig_throws() { + fun validFutureConfig_doesNotThrow() { lint() - .files((xml("res/raw/safety_center_config.xml", "<invalid-root/>"))) + .files( + xml("res/raw-99/safety_center_config.xml", VALID_TIRAMISU_CONFIG), + STRINGS_WITH_REFERENCE_XML) + .run() + .expectClean() + } + + @Test + fun invalidConfigWithSingleSchema_throwsOneError() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE + lint() + .files(xml("res/raw-v34/safety_center_config.xml", "<invalid-root/>")) .run() .expect( - "res/raw/safety_center_config.xml: Error: Parser exception: " + + "res/raw-v34/safety_center_config.xml: Error: Parser exception at sdk=34: " + "\"Element safety-center-config missing\", cause: \"null\" " + "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings") } @Test - fun unrelatedFile_doesNotThrow() { + fun invalidConfigWithMultipleSchemas_throwsMultipleErrors() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE lint() - .files((xml("res/raw/some_other_config.xml", "<some-other-root/>"))) + .files(xml("res/raw/safety_center_config.xml", "<invalid-root/>")) .run() - .expectClean() + .expect( + "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " + + "\"Element safety-center-config missing\", cause: \"null\" " + + "[InvalidSafetyCenterConfig]\nres/raw/safety_center_config.xml: " + + "Error: Parser exception at sdk=34: " + + "\"Element safety-center-config missing\", cause: \"null\" " + + "[InvalidSafetyCenterConfig]\n2 errors, 0 warnings") + } + + @Test + fun validUConfigWithUFields_throwsInT() { + FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE + lint() + .files( + xml("res/raw/safety_center_config.xml", VALID_UDC_CONFIG), + STRINGS_WITH_REFERENCE_XML) + .run() + .expect( + "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " + + "\"Unexpected attribute dynamic-safety-source.notificationsAllowed\", cause: " + + "\"null\" [InvalidSafetyCenterConfig]\n1 errors, 0 warnings") + } + + @Test + fun unrelatedFile_doesNotThrow() { + lint().files(xml("res/raw/some_other_config.xml", "<some-other-root/>")).run().expectClean() } @Test fun unrelatedFolder_doesNotThrow() { - lint().files((xml("res/values/strings.xml", "<some-other-root/>"))).run().expectClean() + lint().files(xml("res/values/strings.xml", "<some-other-root/>")).run().expectClean() + } + + private companion object { + val STRINGS_WITH_REFERENCE_XML: TestFile = + xml( + "res/values/strings.xml", + """ +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="reference" translatable="false">Reference</string> +</resources> + """) + + const val VALID_TIRAMISU_CONFIG = + """ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="group" + title="@lint.test.pkg:string/reference" + summary="@lint.test.pkg:string/reference"> + <static-safety-source + id="source" + title="@lint.test.pkg:string/reference" + summary="@lint.test.pkg:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> + """ + + const val VALID_UDC_CONFIG = + """ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="group" + title="@lint.test.pkg:string/reference" + summary="@lint.test.pkg:string/reference"> + <dynamic-safety-source + id="source" + packageName="package" + title="@lint.test.pkg:string/reference" + summary="@lint.test.pkg:string/reference" + intentAction="intent" + profile="primary_profile_only" + notificationsAllowed="true"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> + """ } } diff --git a/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml new file mode 100644 index 000000000..e9f4f91a6 --- /dev/null +++ b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml @@ -0,0 +1,111 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="AndroidLockScreenSources" + title="@com.android.safetycenter.resources:string/lock_screen_sources_title" + summary="@com.android.safetycenter.resources:string/lock_screen_sources_summary"> + <dynamic-safety-source + id="AndroidLockScreen" + packageName="com.android.settings" + profile="primary_profile_only" + title="@com.android.safetycenter.resources:string/lock_screen_title" + summary="@com.android.safetycenter.resources:string/lock_screen_summary_disabled" + searchTerms="@com.android.safetycenter.resources:string/lock_screen_search_terms" + initialDisplayState="disabled"/> + <dynamic-safety-source + id="AndroidBiometrics" + packageName="com.android.settings" + profile="primary_profile_only" + title="@com.android.safetycenter.resources:string/biometrics_title" + searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms" + initialDisplayState="hidden"/> + </safety-sources-group> + <safety-sources-group + id="AndroidPrivacySources" + title="@com.android.safetycenter.resources:string/privacy_sources_title" + summary="@com.android.safetycenter.resources:string/privacy_sources_summary" + statelessIconType="privacy"> + <static-safety-source + id="AndroidPermissionUsage" + profile="primary_profile_only" + intentAction="android.intent.action.REVIEW_PERMISSION_USAGE" + title="@com.android.safetycenter.resources:string/permission_usage_title" + summary="@com.android.safetycenter.resources:string/permission_usage_summary" + searchTerms="@com.android.safetycenter.resources:string/permission_usage_search_terms"/> + <static-safety-source + id="AndroidPermissionManager" + profile="primary_profile_only" + intentAction="android.intent.action.MANAGE_PERMISSIONS" + title="@com.android.safetycenter.resources:string/permission_manager_title" + summary="@com.android.safetycenter.resources:string/permission_manager_summary" + searchTerms="@com.android.safetycenter.resources:string/permission_manager_search_terms"/> + <static-safety-source + id="AndroidPrivacyControls" + profile="primary_profile_only" + intentAction="android.settings.PRIVACY_CONTROLS" + title="@com.android.safetycenter.resources:string/privacy_controls_title" + summary="@com.android.safetycenter.resources:string/privacy_controls_summary" + searchTerms="@com.android.safetycenter.resources:string/privacy_controls_search_terms"/> + <issue-only-safety-source + id="AndroidAccessibility" + packageName="com.android.permissioncontroller" + profile="all_profiles" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidNotificationListener" + packageName="com.android.permissioncontroller" + profile="primary_profile_only" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidBackgroundLocation" + packageName="com.android.permissioncontroller" + profile="all_profiles" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidPermissionAutoRevoke" + packageName="com.android.permissioncontroller" + profile="all_profiles" + refreshOnPageOpenAllowed="true"/> + </safety-sources-group> + <safety-sources-group + id="AndroidAdvancedSources" + title="@com.android.safetycenter.resources:string/advanced_title"> + <dynamic-safety-source + id="AndroidWorkPolicyInfo" + packageName="com.android.permissioncontroller" + profile="primary_profile_only" + initialDisplayState="hidden" + refreshOnPageOpenAllowed="true"/> + <static-safety-source + id="AndroidAdvancedSecurity" + profile="primary_profile_only" + intentAction="com.android.settings.security.SECURITY_ADVANCED_SETTINGS" + title="@com.android.safetycenter.resources:string/advanced_security_title" + summary="@com.android.safetycenter.resources:string/advanced_security_summary" + searchTerms="@com.android.safetycenter.resources:string/advanced_security_search_terms"/> + <static-safety-source + id="AndroidAdvancedPrivacy" + profile="primary_profile_only" + intentAction="android.settings.PRIVACY_ADVANCED_SETTINGS" + title="@com.android.safetycenter.resources:string/advanced_privacy_title" + summary="@com.android.safetycenter.resources:string/advanced_privacy_summary" + searchTerms="@com.android.safetycenter.resources:string/advanced_privacy_search_terms"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Resources/shared_res/values-af/strings.xml b/SafetyCenter/Resources/shared_res/values-af/strings.xml index 09a89fb15..d35ccadf1 100644 --- a/SafetyCenter/Resources/shared_res/values-af/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-af/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Gaan instellingslys na"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Toestel is dalk in gevaar"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Toestel is in gevaar"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Jy is dalk in gevaar"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jy is in gevaar"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jou data is dalk in gevaar"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jou data is in gevaar"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Wagwoorde is gekompromitteer (oud)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Wagwoorde is gekompromitteer (nuut)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Jy is dalk in gevaar"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jy is in gevaar"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensiële risiko’s gevind"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko’s gevind"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Rekening is dalk in gevaar"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Rekening is in gevaar"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# opletberig}other{# opletberigte}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-am/strings.xml b/SafetyCenter/Resources/shared_res/values-am/strings.xml index a52c37c04..028d60fae 100644 --- a/SafetyCenter/Resources/shared_res/values-am/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-am/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"የቅንብሮች ዝርዝርን ይፈትሹ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"መሣሪያ አደጋ ላይ ሊሆን ይችላል"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"መሣሪያ አደጋ ላይ ነው"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ተጋላጭ ሊሆኑ ይችላሉ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ተጋላጭ ነዎት"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ውሂብዎ አደጋ ላይ ሊሆን ይችላል"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ውሂብዎ አደጋ ላይ ነው"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"የይለፍ ቃላት ተጠልፈዋል (የድሮ)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"የይለፍ ቃላት ተጠልፈዋል (አዲስ)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"አደጋ ላይ ሊሆኑ ይችላሉ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"አደጋ ላይ ነዎት"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ሊከሰቱ የሚችሉ አደጋዎች ተገኝተዋል"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"አደጋዎች ተገኝተዋል"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"መለያ አደጋ ላይ ሊሆን ይችላል"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"መለያ አደጋ ላይ ነው"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ማንቂያ}one{# ማንቂያ}other{# ማንቂያዎች}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ar/strings.xml b/SafetyCenter/Resources/shared_res/values-ar/strings.xml index 8ff93f51f..45f632f9d 100644 --- a/SafetyCenter/Resources/shared_res/values-ar/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ar/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"الاطّلاع على قائمة الإعدادات"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"الجهاز قد يكون معرّضًا للخطر"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"الجهاز معرّض للخطر"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"قد تكون معرّضًا للخطر"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"أنت معرّض للخطر"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"قد تكون بياناتك معرّضة للخطر"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"بياناتك معرّضة للخطر"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"قد يكون الحساب معرّضًا للخطر"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"الحساب معرّض للخطر"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{تنبيه واحد (#)}zero{# تنبيه}two{تنبيهان}few{# تنبيهات}many{# تنبيهًا}other{# تنبيه}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-as/strings.xml b/SafetyCenter/Resources/shared_res/values-as/strings.xml index 7daf9c7ff..b1d006597 100644 --- a/SafetyCenter/Resources/shared_res/values-as/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-as/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ছেটিঙৰ সূচী পৰীক্ষা কৰক"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ডিভাইচটোৰ বিপদাশংকা থাকিব পাৰে"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ডিভাইচটো অসুৰক্ষিত অৱস্থাত আছে"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"আপুনি আশংকাত থাকিব পাৰে"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"আপুনি আশংকাত আছে"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"আপোনাৰ ডেটাখিনিৰ বিপদাশংকা থাকিব পাৰে"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"আপোনাৰ ডেটাখিনিৰ বিপদাশংকা আছে"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"পাছৱৰ্ড হেক কৰা হৈছে (পুৰণি)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"পাছৱৰ্ড হেক কৰা হৈছে (নতুন)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"একাউণ্টত বিপদাশংকা থাকিব পাৰে"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"আপোনাৰ একাউণ্টত বিপদাশংকা আছে"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"সম্ভাব্য বিপদাশংকা পোৱা গৈছে"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"বিপদাশংকা পোৱা গৈছে"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"একাউণ্টটোত বিপদাশংকা থাকিব পাৰে"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"একাউণ্টৰ বিপদাশংকা আছে"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# টা সতৰ্কবাৰ্তা}one{# সতৰ্কবাৰ্তা}other{# সতৰ্কবাৰ্তা}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-az/strings.xml b/SafetyCenter/Resources/shared_res/values-az/strings.xml index 20cebbaab..96510a6cb 100644 --- a/SafetyCenter/Resources/shared_res/values-az/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-az/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ayarlar siyahısını yoxlayın"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Cihaz risk altında ola bilər"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Cihaz risk altındadır"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risk altında ola bilərsiniz"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risk altındasınız"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Datanız risk altında ola bilər"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datanız risk altındadır"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parollar oğurlanıb (köhnə)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parollar oğurlanıb (yeni)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risk altında ola bilərsiniz"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risk altındasınız"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensial risklər tapıldı"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risklər tapıldı"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hesab risk altında ola bilər"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hesab risk altındadır"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# siqnal}other{# siqnal}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml index ce6247204..30b65051c 100644 --- a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Proverite listu podešavanja"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj je možda ugrožen"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je ugrožen"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Možda ste ugroženi"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ugroženi ste"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podaci su možda ugroženi"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podaci su ugroženi"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Lozinke su ugrožene (stare)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Lozinke su ugrožene (nove)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Možda ste ugroženi"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ugroženi ste"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Nalog je možda ugrožen"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Nalog je ugrožen"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# obaveštenje}one{# obaveštenje}few{# obaveštenja}other{# obaveštenja}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-be/strings.xml b/SafetyCenter/Resources/shared_res/values-be/strings.xml index d57336aca..e76aefac5 100644 --- a/SafetyCenter/Resources/shared_res/values-be/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-be/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Праверыць спіс налад"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Прылада можа быць у небяспецы"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Прылада ў небяспецы"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Вы можаце быць у небяспецы"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Вы ў небяспецы"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Вашы даныя могуць быць у небяспецы"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Вашы даныя ў небяспецы"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Раскрытыя паролі (старыя)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Раскрытыя паролі (новыя)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Вы можаце быць у небяспецы"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Вы ў небяспецы"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Выяўлена патэнцыяльная небяспека"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Выяўлена небяспека"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Уліковы запіс можа быць у небяспецы"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Уліковы запіс у небяспецы"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# абвестка}one{# абвестка}few{# абвесткі}many{# абвестак}other{# абвесткі}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-bg/strings.xml b/SafetyCenter/Resources/shared_res/values-bg/strings.xml index 1f0175973..ad13d485a 100644 --- a/SafetyCenter/Resources/shared_res/values-bg/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-bg/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверка на списъка с настройки"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"У-вото може да е изложено на риск"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Устройството е изложено на риск"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Има потенциален риск за вас"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Има риск за вас"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Данните може да са изложени на риск"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Данните ви са изложени на риск"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Компрометирани пароли (стари)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Компрометирани пароли (нови)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Има потенциален риск за вас"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Има риск за вас"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Открити са потенциални рискове"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Открити са рискове"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Профилът може да е изложен на риск"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Профилът е изложен на риск"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сигнал}other{# сигнала}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-bn/strings.xml b/SafetyCenter/Resources/shared_res/values-bn/strings.xml index 290b2fed7..7646005ac 100644 --- a/SafetyCenter/Resources/shared_res/values-bn/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-bn/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"সেটিংস তালিকা চেক করুন"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ডিভাইসের নিরাপত্তা বিপন্ন হতে পারে"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ডিভাইসের নিরাপত্তা বিপন্ন"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"আপনার নিরাপত্তা বিপন্ন হতে পারে"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"আপনার নিরাপত্তা বিপন্ন"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"আপনার ডেটার হয়ত ক্ষতি হতে পারে"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"আপনার ডেটার ক্ষতি হতে পারে"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"পাসওয়ার্ড চুরি হয়ে গেছে (পুরনো)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"পাসওয়ার্ড চুরি হয়ে গেছে (নতুন)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"আপনার নিরাপত্তা বিপন্ন হতে পারে"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"আপনার নিরাপত্তা বিপন্ন"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"সম্ভাব্য ঝুঁকি খুঁজে পাওয়া গেছে"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ঝুঁকির বিষয়টি খুঁজে পাওয়া গেছে"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"অ্যাকাউন্টের হয়ত ক্ষতি হতে পারে"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"অ্যাকাউন্টের ক্ষতি হতে পারে"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{#টি সতর্কতা}one{#টি সতর্কতা}other{#টি সতর্কতা}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-bs/strings.xml b/SafetyCenter/Resources/shared_res/values-bs/strings.xml index 96d792479..62c542eaa 100644 --- a/SafetyCenter/Resources/shared_res/values-bs/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-bs/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Provjerite listu postavki"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj bi mogao biti izložen riziku"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je izložen riziku"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Moguće je da ste izloženi riziku"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Izloženi ste riziku"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vaši podaci bi mogli biti izloženi riziku"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vaši podaci su izloženi riziku"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Lozinke su ugrožene (stare)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Lozinke su ugrožene (nove)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Možda ste izloženi riziku"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Izloženi ste riziku"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun bi mogao biti izložen riziku"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je izložen riziku"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozorenje}one{# upozorenje}few{# upozorenja}other{# upozorenja}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ca/strings.xml b/SafetyCenter/Resources/shared_res/values-ca/strings.xml index adc1f58c9..c674147aa 100644 --- a/SafetyCenter/Resources/shared_res/values-ca/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ca/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Comprova la llista de configuració"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositiu pot estar en perill"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositiu està en perill"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pot ser que estiguis en perill"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estàs en perill"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Les dades poden estar en perill"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Les dades estan en perill"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"El compte pot estar en perill"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Aquest compte està en perill"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alertes}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-cs/strings.xml b/SafetyCenter/Resources/shared_res/values-cs/strings.xml index a7ab9bcd4..603087524 100644 --- a/SafetyCenter/Resources/shared_res/values-cs/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-cs/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Zkontrolujte seznam nastavení"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Zařízení může být ohroženo"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Zařízení je ohroženo"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Můžete být ohroženi"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jste v ohrožení"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data mohou být ohrožena"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data jsou ohrožena"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Prolomená hesla (staré)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Prolomená hesla (nové)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Můžete být ohroženi"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jste v ohrožení"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Byla nalezena možná rizika"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Byla nalezena rizika"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Účet může být ohrožen"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Účet je ohrožen"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozornění}few{# upozornění}many{# upozornění}other{# upozornění}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-da/strings.xml b/SafetyCenter/Resources/shared_res/values-da/strings.xml index 8745e6096..634d5d483 100644 --- a/SafetyCenter/Resources/shared_res/values-da/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-da/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tjek listen over indstillinger"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheden kan være sårbar"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheden er sårbar"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du er muligvis i fare"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du er i fare"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dine data kan være sårbare"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dine data er sårbare"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Kompromitterede adgangskoder (gamle)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Kompromitterede adgangskoder (nye)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Din konto er muligvis sårbar"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Din konto er sårbar"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Der blev fundet potentielle risici"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Der blev fundet risici"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontoen kan være sårbar"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontoen er sårbar"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# underretning}one{# underretning}other{# underretninger}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-de/strings.xml b/SafetyCenter/Resources/shared_res/values-de/strings.xml index 0b7c27277..a6872dc67 100644 --- a/SafetyCenter/Resources/shared_res/values-de/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-de/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Liste der Einstellungen prüfen"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Gerät ist eventuell gefährdet"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Gerät ist gefährdet"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du bist möglicherweise gefährdet"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du bist gefährdet"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Daten eventuell gefährdet"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Daten gefährdet"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"(Alte) Passwörter gefährdet"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"(Neue) Passwörter gefährdet"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Du bist möglicherweise gefährdet"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Du bist gefährdet"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Mögliche Sicherheitsrisiken gefunden"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Sicherheitsrisiken gefunden"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto eventuell gefährdet"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto gefährdet"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# Warnung}other{# Warnungen}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-el/strings.xml b/SafetyCenter/Resources/shared_res/values-el/strings.xml index b88c8b3e8..6c98e0816 100644 --- a/SafetyCenter/Resources/shared_res/values-el/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-el/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Έλεγχος λίστας ρυθμίσεων"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Η συσκευή μπορεί να κινδυνεύει"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Η συσκευή κινδυνεύει"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Ενδέχεται να βρίσκεστε σε κίνδυνο"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Βρίσκεστε σε κίνδυνο"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Τα δεδομένα σας μπορεί να κινδυν."</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Τα δεδομένα σας κινδυνεύουν"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Παραβιάστηκαν κωδ. πρόσβ. (παλιοί)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Παραβιάστηκαν κωδ. πρόσβασης (νέοι)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Ενδέχεται να βρίσκεστε σε κίνδυνο"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Βρίσκεστε σε κίνδυνο"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Εντοπίστηκαν πιθανοί κίνδυνοι"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Εντοπίστηκαν κίνδυνοι"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Ο λογαριασμός μπορεί να κινδυνεύει"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Ο λογαριασμός βρίσκεται σε κίνδυνο"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ειδοποίηση}other{# ειδοποιήσεις}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml index 6137c2caa..26967f2da 100644 --- a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml index 588409f4f..c26f0d40e 100644 --- a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml index 6137c2caa..26967f2da 100644 --- a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml index 6137c2caa..26967f2da 100644 --- a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml index 3058d9c54..ba21dba30 100644 --- a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml index 2e5146f38..2440fa870 100644 --- a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifica la lista de configuración"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositivo podría estar en riesgo"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositivo está en riesgo"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Es posible que estés en peligro"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en peligro"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Tus datos podrían estar en riesgo"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Tus datos están en riesgo"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contraseñas comprometidas (viejas)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contraseñas comprometidas (nuevas)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Es posible que estés en peligro"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Estás en peligro"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Se detectaron riesgos potenciales"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Se detectaron riesgos"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"La cuenta podría estar en riesgo"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"La cuenta está en riesgo"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-es/strings.xml b/SafetyCenter/Resources/shared_res/values-es/strings.xml index 255f424e8..78fc55428 100644 --- a/SafetyCenter/Resources/shared_res/values-es/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-es/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Comprueba la lista de ajustes"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositivo puede estar en riesgo"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositivo está en riesgo"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Puedes estar en riesgo"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en riesgo"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Tus datos pueden estar en riesgo"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Tus datos están en riesgo"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contraseñas vulneradas (antiguas)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contraseñas vulneradas (nuevas)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Puedes estar en riesgo"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Estás en riesgo"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Posibles riesgos detectados"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riesgos detectados"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"La cuenta puede estar en riesgo"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"La cuenta está en riesgo"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-et/strings.xml b/SafetyCenter/Resources/shared_res/values-et/strings.xml index fdf25c6e2..f8eec7fc7 100644 --- a/SafetyCenter/Resources/shared_res/values-et/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-et/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollige seadete loendit"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Seade võib olla ohus"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Seade on ohus"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Võite olla ohus"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Olete ohus"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Teie andmed võivad ohus olla"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Teie andmed on ohus"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Paroolid on ohus (vana)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Paroolid on ohus (uus)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Võite olla ohus"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Olete ohus"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Leiti võimalikud ohud"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Leiti ohud"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto võib olla ohus"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto on ohus"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# hoiatus}other{# hoiatust}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-eu/strings.xml b/SafetyCenter/Resources/shared_res/values-eu/strings.xml index 31ee60f97..54d7f98fc 100644 --- a/SafetyCenter/Resources/shared_res/values-eu/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-eu/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Egiaztatu ezarpenen zerrenda"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Baliteke gailua arriskuan egotea"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Gailua arriskuan dago"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Baliteke arriskuan egotea"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Arriskuan zaude"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Baliteke datuak arriskuan egotea"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datuak arriskuan daude"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Pasahitz zaharrak arriskuan daude"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Pasahitz berriak arriskuan daude"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Baliteke arriskuan egotea"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Arriskuan zaude"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Balizko arriskuak aurkitu dira"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Arriskuak aurkitu dira"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Baliteke kontua arriskuan egotea"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontua arriskuan dago"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alerta}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-fa/strings.xml b/SafetyCenter/Resources/shared_res/values-fa/strings.xml index f76e1c43f..35babbd8e 100644 --- a/SafetyCenter/Resources/shared_res/values-fa/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-fa/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"بررسی فهرست تنظیمات"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"دستگاه ممکن است درمعرض خطر باشد"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"دستگاه درمعرض خطر است"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ممکن است درمعرض خطر باشید"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"درمعرض خطر هستید"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"شاید دادههایتان درمعرض خطر باشد"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"دادههایتان درمعرض خطر است"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"گذرواژهها لو رفته است (قدیمی)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"گذرواژهها لو رفته است (جدید)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ممکن است درمعرض خطر باشید"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"درمعرض خطر هستید"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"خطرات احتمالی پیدا شده است"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"خطراتی پیدا شده است"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ممکن است حساب درمعرض خطر باشد"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"حساب درمعرض خطر است"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# هشدار}one{# هشدار}other{# هشدار}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-fi/strings.xml b/SafetyCenter/Resources/shared_res/values-fi/strings.xml index 1caece7d6..a2dea42e3 100644 --- a/SafetyCenter/Resources/shared_res/values-fi/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-fi/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tarkista asetuslista"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Laite saattaa olla vaarantunut"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Laite on vaarantunut"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Turvallisuutesi on voinut vaarantua"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Turvallisuutesi on vaarantanut"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data voi olla vaarantunut"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data on vaarantunut"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Tili voi olla vaarantunut"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Tili on vaarantunut"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# hälytys}other{# hälytystä}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml index df8088492..64a690116 100644 --- a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Vérifiez la liste des paramètres"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"L\'appareil pourrait être en danger"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"L\'appareil est en danger"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vous pourriez être en danger"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vous êtes en danger"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vos données pourraient être en danger"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vos données sont en danger"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mots de passe compromis (ancien)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mots de passe compromis (nouveau)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Vous pourriez être en danger"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Vous êtes en danger"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Dangers potentiels trouvés"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Dangers trouvés"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Le compte pourrait être en danger"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Le compte est en danger"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerte}one{# alerte}many{# alertes}other{# alertes}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-fr/strings.xml b/SafetyCenter/Resources/shared_res/values-fr/strings.xml index 63c15e508..e1e716fd1 100644 --- a/SafetyCenter/Resources/shared_res/values-fr/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-fr/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Vérifier la liste des paramètres"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Risque potentiel sur l\'appareil"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Risque sur l\'appareil"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risque potentiel"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risque de sécurité"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Risque potentiel sur vos données"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Risque sur vos données"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mots de passe compromis (anciens)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mots de passe compromis (nouveaux)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risque potentiel"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risque de sécurité"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Risques potentiels détectés"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risques détectés"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Risque potentiel sur le compte"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Risque sur le compte"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerte}one{# alerte}many{# alertes}other{# alertes}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-gl/strings.xml b/SafetyCenter/Resources/shared_res/values-gl/strings.xml index e8538b7a6..fd2df84f4 100644 --- a/SafetyCenter/Resources/shared_res/values-gl/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-gl/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Revisa a lista de opcións de configuración"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar en risco"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está en risco"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pode que esteas en risco"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en risco"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Os teus datos poden estar en risco"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Os teus datos están en risco"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contrasinais vulnerados (antigos)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contrasinais vulnerados (novos)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Pode que a túa conta estea en risco"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"A túa conta está en risco"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Atopáronse posibles riscos"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Atopáronse riscos"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar en risco"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está en risco"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-gu/strings.xml b/SafetyCenter/Resources/shared_res/values-gu/strings.xml index 60ecc2ee6..85afd571b 100644 --- a/SafetyCenter/Resources/shared_res/values-gu/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-gu/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"સેટિંગની સૂચિ ચેક કરો"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ડિવાઇસ કદાચ જોખમમાં હોઈ શકે"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ડિવાઇસ જોખમમાં છે"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"તમારી સુરક્ષા જોખમમાં હોઈ શકે છે"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"તમારી સુરક્ષા જોખમમાં છે"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"તમારો ડેટા જોખમમાં હોઈ શકે છે"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"તમારો ડેટા જોખમમાં છે"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"પાસવર્ડ સાથે ચેડાં થયા (જૂના)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"પાસવર્ડ સાથે ચેડાં થયા (નવા)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"તમારી સુરક્ષા જોખમમાં હોઈ શકે છે"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"તમારી સુરક્ષા જોખમમાં છે"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"સંભવિત જોખમો મળ્યા"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"જોખમો મળ્યા"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"એકાઉન્ટ જોખમમાં હોઈ શકે છે"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"એકાઉન્ટ જોખમમાં છે"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# અલર્ટ}one{# અલર્ટ}other{# અલર્ટ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-hi/strings.xml b/SafetyCenter/Resources/shared_res/values-hi/strings.xml index e264fac2c..e69dfc05b 100644 --- a/SafetyCenter/Resources/shared_res/values-hi/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-hi/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिंग की सूची देखें"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिवाइस की सुरक्षा शायद खतरे में है"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिवाइस की सुरक्षा खतरे में है"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"शायद आपकी सुरक्षा खतरे में है"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"आपकी सुरक्षा खतरे में है"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"डेटा की सुरक्षा खतरे में हो सकती है"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"डेटा की सुरक्षा खतरे में है"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"पासवर्ड हैक हो गया (पुराना)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"पासवर्ड हैक हो गया (नया)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"शायद आपकी सुरक्षा खतरे में है"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"आपकी सुरक्षा खतरे में है"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"संभावित खतरे का पता चला"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"खतरे का पता चला"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाते की सुरक्षा खतरे में हो सकती है"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाते की सुरक्षा खतरे में है"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# चेतावनी}one{# चेतावनी}other{# चेतावनियां}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-hr/strings.xml b/SafetyCenter/Resources/shared_res/values-hr/strings.xml index 2c89f1a39..e345b9bd0 100644 --- a/SafetyCenter/Resources/shared_res/values-hr/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-hr/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Provjera popisa postavki"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj je možda ugrožen"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je ugrožen"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vaša je sigurnost možda ugrožena"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vaša je sigurnost ugrožena"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podaci mogu biti ugroženi"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podaci su ugroženi"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Zaporke su ugrožene (staro)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Zaporke su ugrožene (novo)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Vaša je sigurnost možda ugrožena"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Vaša je sigurnost ugrožena"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun je možda ugrožen"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je ugrožen"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozorenje}one{# upozorenje}few{# upozorenja}other{# upozorenja}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-hu/strings.xml b/SafetyCenter/Resources/shared_res/values-hu/strings.xml index 23de9b2fd..e3e26408c 100644 --- a/SafetyCenter/Resources/shared_res/values-hu/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-hu/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ellenőrizze a beállításlistát"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Az eszközhasználat kockázatos lehet"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Az eszköz használata kockázatos"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Veszélyben lehet"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Veszélyben van"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Adatai veszélyben lehetnek"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Adatai veszélyben vannak"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Feltört jelszavak (régi)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Feltört jelszavak (új)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Veszélyben lehet"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Veszélyben van"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Lehetséges veszélyeket észleltünk"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Veszélyeket észleltünk"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Fiókja veszélyben lehet"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Fiókja veszélyben van"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# értesítés}other{# értesítés}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-hy/strings.xml b/SafetyCenter/Resources/shared_res/values-hy/strings.xml index 91d243deb..3f4f9e358 100644 --- a/SafetyCenter/Resources/shared_res/values-hy/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-hy/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ստուգեք կարգավորումների ցանկը"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Սարքը կարող է վտանգված լինել"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Սարքը վտանգված է"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Դուք կարող եք վտանգի տակ լինել"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Դուք վտանգի տակ եք"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Տվյալները կարող են վտանգված լինել"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ձեր տվյալները վտանգված են"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Հին գաղտնաբառերը կոտրված են"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Նոր գաղտնաբառերը կոտրված են"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Դուք կարող եք վտանգված լինել"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Դուք վտանգված եք"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Հնարավոր ռիսկեր են հայտնաբերվել"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Ռիսկեր են հայտնաբերվել"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Հաշիվը կարող է վտանգված լինել"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Հաշիվը վտանգված է"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-in/strings.xml b/SafetyCenter/Resources/shared_res/values-in/strings.xml index f7ac72208..8c0b3a268 100644 --- a/SafetyCenter/Resources/shared_res/values-in/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-in/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Periksa daftar setelan"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Perangkat mungkin berisiko"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Perangkat berisiko"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Anda mungkin berisiko"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Anda berisiko"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data Anda mungkin berisiko"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data Anda berisiko"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Sandi disusupi (lama)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Sandi disusupi (baru)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Anda mungkin berisiko"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Anda berisiko"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensi risiko ditemukan"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko ditemukan"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Akun mungkin berisiko"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akun berisiko"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# peringatan}other{# peringatan}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-is/strings.xml b/SafetyCenter/Resources/shared_res/values-is/strings.xml index fc5ef0e99..b003327b9 100644 --- a/SafetyCenter/Resources/shared_res/values-is/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-is/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Athuga lista yfir stillingar"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Tækið er hugsanlega í hættu"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Tækið er í hættu"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Þú gætir verið í hættu"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Þú ert í hættu"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Gögnin þín gætu verið í hættu"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Gögnin þín eru í hættu"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Aðgangsorð í hættu (gömul)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Aðgangsorð í hættu (ný)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Þú gætir verið í hættu"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Þú ert í hættu"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Hugsanleg hætta greindist"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Hætta greindist"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Reikningurinn er hugsanlega í hættu"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Reikningurinn er í hættu"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# viðvörun}one{# viðvörun}other{# viðvaranir}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-it/strings.xml b/SafetyCenter/Resources/shared_res/values-it/strings.xml index 2b5ee3092..7535a163a 100644 --- a/SafetyCenter/Resources/shared_res/values-it/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-it/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Controlla l\'elenco di impostazioni"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Il dispositivo potrebbe essere a rischio"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Il dispositivo è a rischio"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Potresti essere a rischio"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Sei a rischio"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"I dati potrebbero essere a rischio"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"I dati sono a rischio"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Password compromesse (precedenti)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Password compromesse (nuove)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Potresti essere a rischio"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Sei a rischio"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potenziali rischi rilevati"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Rischi rilevati"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"L\'account potrebbe essere a rischio"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"L\'account è a rischio"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# avviso}many{# avvisi}other{# avvisi}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-iw/strings.xml b/SafetyCenter/Resources/shared_res/values-iw/strings.xml index 8f02b6572..e5f2c15fb 100644 --- a/SafetyCenter/Resources/shared_res/values-iw/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-iw/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"בדיקה של רשימת ההגדרות"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"המכשיר עלול להיות בסיכון"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"המכשיר בסיכון"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ייתכן שקיים סיכון"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"קיים סיכון"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ייתכן שהנתונים שלך בסיכון"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"הנתונים שלך בסיכון"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"סיסמאות בסכנת חשיפה (ישנות)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"סיסמאות בסכנת חשיפה (חדשות)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ייתכן שקיים סיכון"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"קיים סיכון"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"נמצאו סכנות אפשריות"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"נמצאו סיכונים"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"החשבון עלול להיות בסיכון"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"החשבון בסיכון"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{התראה #}two{# התראות}many{# התראות}other{# התראות}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ja/strings.xml b/SafetyCenter/Resources/shared_res/values-ja/strings.xml index 16dde305e..af733879f 100644 --- a/SafetyCenter/Resources/shared_res/values-ja/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ja/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"設定リストをご確認ください"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"デバイスが危険な可能性があります"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"デバイスが危険です"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"危険な可能性があります"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"危険にさらされています"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"データが危険な可能性があります"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"データが危険です"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"古いパスワードの不正使用"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"新しいパスワードの不正使用"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"危険にさらされています"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"危険にさらされています"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"潜在的なリスクが検出されました"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"リスクが検出されました"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"アカウントが危険な可能性があります"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"アカウントが危険です"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 件のアラート}other{# 件のアラート}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ka/strings.xml b/SafetyCenter/Resources/shared_res/values-ka/strings.xml index 82eda0161..bf1471501 100644 --- a/SafetyCenter/Resources/shared_res/values-ka/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ka/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"პარამეტრების სიის შემოწმება"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"მოწყობილობა შესაძლო საფრთხის ქვეშაა"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"მოწყობილობა საფრთხის ქვეშაა"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"თქვენ შეიძლება საფრთხე გემუქრებოდეთ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"თქვენ საფრთხეში ხართ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"თქვ. მონაც. შესაძლოა საფრთხე ემუქრ."</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"თქვენ მონაცემებს საფრთხე ემუქრება"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"პაროლები კომპრომეტირებულია (ძველი)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"პაროლები გატეხილია (ახალი)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"თქვენ შეიძლება საფრთხე გემუქრებოდეთ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"თქვენ საფრთხეში ხართ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ნაპოვნია პოტენციური რისკები"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"აღმოჩენილი რისკები"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ანგარიშს შესაძლოა საფრთხე ემუქრება"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ანგარიშს საფრთხე ემუქრება"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# გაფრთხილება}other{# გაფრთხილება}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-kk/strings.xml b/SafetyCenter/Resources/shared_res/values-kk/strings.xml index 25ac190b5..a9d11d0b3 100644 --- a/SafetyCenter/Resources/shared_res/values-kk/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-kk/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Параметрлер тізімін тексеру"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Құрылғыға қауіп төнген болуы мүмкін"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Құрылғыға қауіп төніп тұр"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Қауіп төнуі мүмкін"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Қауіп төніп тұр"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Деректерге қауіп төнген сияқты"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Деректерге қауіп төніп тұр"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунтқа қауіп төнген сияқты"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунтқа қауіп төніп тұр"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-km/strings.xml b/SafetyCenter/Resources/shared_res/values-km/strings.xml index 30b60cf50..a36b062a4 100644 --- a/SafetyCenter/Resources/shared_res/values-km/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-km/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ពិនិត្យបញ្ជីការកំណត់"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ឧបករណ៍អាចប្រឈមនឹងហានិភ័យ"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ឧបករណ៍កំពុងប្រឈមនឹងហានិភ័យ"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"អ្នកអាចប្រឈមនឹងហានិភ័យ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"អ្នកប្រឈមនឹងហានិភ័យ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ទិន្នន័យរបស់អ្នកអាចប្រឈមនឹងហានិភ័យ"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ទិន្នន័យរបស់អ្នកគឺប្រឈមនឹងហានិភ័យ"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ពាក្យសម្ងាត់រងការលុកលុយ (ចាស់)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ពាក្យសម្ងាត់រងការលុកលុយ (ថ្មី)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"អ្នកអាចប្រឈមនឹងហានិភ័យ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"អ្នកប្រឈមនឹងហានិភ័យ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"រកឃើញហានិភ័យដែលអាចមាន"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"រកឃើញហានិភ័យ"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"គណនីអាចប្រឈមនឹងហានិភ័យ"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"គណនីប្រឈមនឹងហានិភ័យ"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{ការជូនដំណឹង #}other{ការជូនដំណឹង #}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-kn/strings.xml b/SafetyCenter/Resources/shared_res/values-kn/strings.xml index ac8fc7349..f8198afe9 100644 --- a/SafetyCenter/Resources/shared_res/values-kn/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-kn/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ಸೆಟ್ಟಿಂಗ್ಗಳ ಪಟ್ಟಿಯನ್ನು ಪರಿಶೀಲಿಸಿ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ಸಾಧನವು ಅಪಾಯದಲ್ಲಿರಬಹುದು"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ಸಾಧನವು ಅಪಾಯದಲ್ಲಿದೆ"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ನೀವು ಅಪಾಯಕ್ಕೀಡಾಗಬಹುದು"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ನೀವು ಅಪಾಯದಲ್ಲಿದ್ದೀರಿ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ನಿಮ್ಮ ಡೇಟಾ ಅಪಾಯದಲ್ಲಿರಬಹುದು"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ನಿಮ್ಮ ಡೇಟಾ ಅಪಾಯದಲ್ಲಿದೆ"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ಅಪಾಯಕ್ಕೀಡಾದ ಪಾಸ್ವರ್ಡ್ಗಳು (ಹಳೆಯ)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ಅಪಾಯಕ್ಕೀಡಾದ ಪಾಸ್ವರ್ಡ್ಗಳು (ಹೊಸತು)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ನೀವು ಅಪಾಯಕ್ಕೀಡಾಗಿರಬಹುದು"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ನೀವು ಅಪಾಯದಲ್ಲಿದ್ದೀರಿ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ಸಂಭವನೀಯ ಅಪಾಯಗಳು ಕಂಡುಬಂದಿವೆ"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ಅಪಾಯಗಳು ಕಂಡುಬಂದಿವೆ"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ಖಾತೆಯು ಅಪಾಯಕ್ಕೀಡಾಗಬಹುದು"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ಖಾತೆ ಅಪಾಯದಲ್ಲಿದೆ"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ಅಲರ್ಟ್}one{# ಅಲರ್ಟ್ಗಳು}other{# ಅಲರ್ಟ್ಗಳು}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ko/strings.xml b/SafetyCenter/Resources/shared_res/values-ko/strings.xml index d49f59620..1674c1707 100644 --- a/SafetyCenter/Resources/shared_res/values-ko/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ko/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"설정 목록 확인"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"기기에 잠재적 위험 발생"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"기기 위험 발생"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"위험에 노출되었을 수 있음"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"위험에 노출됨"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"데이터에 보안 위험이 있을 수 있음"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"데이터에 보안 위험이 있음"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"기존 비밀번호 손상됨"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"신규 비밀번호 손상됨"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"위험에 노출되었을 수 있음"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"위험에 노출됨"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"잠재적 위험 발견됨"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"위험 발견됨"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"계정에 보안 위험이 있을 수 있음"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"계정에 보안 위험이 있음"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{알림 #개}other{알림 #개}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ky/strings.xml b/SafetyCenter/Resources/shared_res/values-ky/strings.xml index 6b66f8086..a29fe358b 100644 --- a/SafetyCenter/Resources/shared_res/values-ky/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ky/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Параметрлердин тизмесин текшерүү"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Түзмөк коркунучта болушу мүмкүн"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Түзмөк коркунучта"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Коопсуздук коркунучта болушу мүмкүн"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Коопсуздук коркунучта"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Маалыматтын коопсуздугу коркунучта"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Маалыматтын коопсуздугу коркунучта"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Сырсөздөр уурдалды (эски)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Сырсөз уурдалды (жаңы)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Коопсуздук коркунучта"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Коопсуздук коркунучта"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Мүмкүн болгон коркунучтар табылды"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Коркунучтар табылды"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунт коркунучта болушу мүмкүн"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунттун коопсуздугу коркунучта"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# эскертүү}other{# эскертүү}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-lo/strings.xml b/SafetyCenter/Resources/shared_res/values-lo/strings.xml index 3c2d7a452..e0f6f0f83 100644 --- a/SafetyCenter/Resources/shared_res/values-lo/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-lo/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ກວດລາຍການການຕັ້ງຄ່າ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ອຸປະກອນອາດມີຄວາມສ່ຽງ"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ອຸປະກອນມີຄວາມສ່ຽງ"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ທ່ານອາດຢູ່ໃນຄວາມສ່ຽງ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ທ່ານຢູ່ໃນຄວາມສ່ຽງ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ຂໍ້ມູນຂອງທ່ານອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ຂໍ້ມູນຂອງທ່ານຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ລະຫັດຜ່ານມີການຮົ່ວໄຫຼ (ເກົ່າ)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ລະຫັດຜ່ານມີການຮົ່ວໄຫຼ (ໃໝ່)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ທ່ານອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ທ່ານຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ພົບຄວາມສ່ຽງທີ່ອາດເກີດຂຶ້ນ"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ພົບຄວາມສ່ຽງ"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ບັນຊີອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ບັນຊີຕົກຢູ່ໃນຄວາມສ່ຽງ"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ແຈ້ງເຕືອນ}other{# ແຈ້ງເຕືອນ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-lt/strings.xml b/SafetyCenter/Resources/shared_res/values-lt/strings.xml index 85045ea9f..bbef2ce6c 100644 --- a/SafetyCenter/Resources/shared_res/values-lt/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-lt/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tikrinti nustatymų sąrašą"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Įrenginiui gali grėsti pavojus"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Įrenginiui gresia pavojus"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Jums gali kilti pavojus"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jums iškilo pavojus"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jūsų duomenims galėjo kilti pavojus"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jūsų duomenims kilo pavojus"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Slaptažodžiai pažeisti (seni)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Slaptažodžiai pažeisti (nauji)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Jums gali kilti pavojus"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jums iškilo pavojus"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Aptikta potencialių pavojų"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Aptikta pavojų"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Paskyrai galėjo kilti pavojus"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Paskyrai kilo pavojus"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# įspėjimas}one{# įspėjimas}few{# įspėjimai}many{# įspėjimo}other{# įspėjimų}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-lv/strings.xml b/SafetyCenter/Resources/shared_res/values-lv/strings.xml index bcd895149..b3aaec710 100644 --- a/SafetyCenter/Resources/shared_res/values-lv/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-lv/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Pārbaudiet iestatījumu sarakstu"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Iespējams, ierīce ir apdraudēta"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Ierīce ir apdraudēta"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Iespējams, jūs esat apdraudēts"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jūs esat apdraudēts"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jūsu dati var būt apdraudēti"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jūsu dati ir apdraudēti"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Uzlauztas paroles (vecas)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Uzlauztas paroles (jaunas)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Iespējams, jūs esat apdraudēts"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jūs esat apdraudēts"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Konstatēts iespējams risks"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Konstatēts risks"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konts var būt apdraudēts"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konts ir apdraudēts"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ieteikums vai brīdinājums}zero{# ieteikumu vai brīdinājumu}one{# ieteikums vai brīdinājums}other{# ieteikumi vai brīdinājumi}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-mk/strings.xml b/SafetyCenter/Resources/shared_res/values-mk/strings.xml index c8aea82e4..446b374d5 100644 --- a/SafetyCenter/Resources/shared_res/values-mk/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-mk/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверете го списокот со поставки"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Уредот можеби е изложен на ризик"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Уредот е изложен на ризик"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можеби сте под ризик"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Под ризик сте"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Податоците можеби се под ризик"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Податоците се под ризик"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Компромитирани лозинки (стари)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Компромитирани лозинки (нови)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Можеби сте под ризик"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Под ризик сте"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Најдени се потенцијални ризици"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Најдени се ризици"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Сметката можеби е под ризик"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Сметката е под ризик"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# предупредување}one{# предупредување}other{# предупредувања}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ml/strings.xml b/SafetyCenter/Resources/shared_res/values-ml/strings.xml index 8674ccbb0..62586aa57 100644 --- a/SafetyCenter/Resources/shared_res/values-ml/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ml/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ക്രമീകരണ ലിസ്റ്റ് പരിശോധിക്കുക"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ഉപകരണം അപകടത്തിലാകാൻ സാധ്യതയുണ്ട്"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ഉപകരണം അപകടത്തിലാണ്"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"നിങ്ങൾ അപകടത്തിലായിരിക്കാം"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"നിങ്ങൾ അപകടത്തിലാണ്"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"നിങ്ങളുടെ ഡാറ്റ അപകടത്തിലായേക്കാം"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"നിങ്ങളുടെ ഡാറ്റ അപകടത്തിലാണ്"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"പാസ്വേഡുകൾ അപഹരിച്ചു (പഴയത്)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"പാസ്വേഡുകൾ അപഹരിച്ചു (പുതിയത്)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"നിങ്ങൾ അപകടത്തിലായിരിക്കാം"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"നിങ്ങൾ അപകടത്തിലാണ്"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"സാധ്യതയുള്ള അപകടസാധ്യതകൾ കണ്ടെത്തി"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"അപകടസാധ്യതകൾ കണ്ടെത്തി"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"അക്കൗണ്ട് അപകടത്തിലായേക്കാം"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"അക്കൗണ്ട് അപകടത്തിലാണ്"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# മുന്നറിയിപ്പ്}other{# മുന്നറിയിപ്പുകൾ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-mn/strings.xml b/SafetyCenter/Resources/shared_res/values-mn/strings.xml index 0a9308cd7..def573378 100644 --- a/SafetyCenter/Resources/shared_res/values-mn/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-mn/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Тохиргооны жагсаалтыг шалгах"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Төхөөрөмж эрсдэлд байж магадгүй"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Төхөөрөмж эрсдэлд байна"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Та эрсдэлд байж магадгүй"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Та эрсдэлд байна"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Таны өгөгдөл эрсдэлд байж магадгүй"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Таны өгөгдөл эрсдэлд байна"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Нууц үг алдагдсан (хуучин)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Нууц үг алдагдсан (шинэ)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Та эрсдэлд орсон байж магадгүй"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Та эрсдэлд орсон байна"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Боломжит эрсдэл илэрсэн"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Эрсдэл илэрсэн"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Бүртгэл эрсдэлд байж магадгүй"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Бүртгэл эрсдэлд байна"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сэрэмжлүүлэг}other{# сэрэмжлүүлэг}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-mr/strings.xml b/SafetyCenter/Resources/shared_res/values-mr/strings.xml index be26bde74..143751f43 100644 --- a/SafetyCenter/Resources/shared_res/values-mr/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-mr/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिंग्जची सूची तपासा"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिव्हाइस धोक्यात असू शकते"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिव्हाइस धोक्यात आहे"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"तुमची सुरक्षा धोक्यात असू शकते"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"तुमची सुरक्षा धोक्यात आहे"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"तुमचा डेटा धोक्यात असू शकतो"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"तुमचा डेटा धोक्यात आहे"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"धोक्यात असलेले पासवर्ड (जुने)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"धोक्यात असलेले पासवर्ड (नवीन)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"तुमची सुरक्षा धोक्यात असू शकते"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"तुमची सुरक्षा धोक्यात आहे"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"संभाव्य धोके आढळले"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"धोके आढळले"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाते धोक्यात असू शकते"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाते धोक्यात आहे"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# इशारा}other{# इशारे}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ms/strings.xml b/SafetyCenter/Resources/shared_res/values-ms/strings.xml index 8e4d6b595..8eb9c1ec5 100644 --- a/SafetyCenter/Resources/shared_res/values-ms/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ms/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Semak senarai tetapan"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Peranti mungkin berisiko"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Peranti berisiko"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Anda mungkin berisiko"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Anda berisiko"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data anda mungkin berisiko"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data anda berisiko"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Kata laluan terjejas (lama)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Kata laluan terjejas (baharu)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Anda mungkin berisiko"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Anda berisiko"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Risiko berpotensi ditemukan"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko ditemukan"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Akaun mungkin berisiko"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akaun berisiko"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# amaran}other{# amaran}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-my/strings.xml b/SafetyCenter/Resources/shared_res/values-my/strings.xml index 79c5fff12..a09b3f664 100644 --- a/SafetyCenter/Resources/shared_res/values-my/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-my/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ဆက်တင်များစာရင်းကို စစ်ဆေးပါ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"စက်တွင် အန္တရာယ်ရှိနိုင်သည်"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"စက်တွင် အန္တရာယ်ရှိနေသည်"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"သင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"သင့်တွင် အန္တရာယ်ရှိနေသည်"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"သင့်ဒေတာတွင် အန္တရာယ်ရှိနိုင်သည်"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"သင့်ဒေတာတွင် အန္တရာယ်ရှိသည်"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"စကားဝှက် ကျိုးပေါက်နေသည် (အဟောင်း)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"စကားဝှက် ကျိုးပေါက်နေသည် (အသစ်)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"သင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"သင့်တွင် အန္တရာယ်ရှိနေသည်"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"အန္တရာယ်ဖြစ်နိုင်ခြေ တွေ့သည်"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"အန္တရာယ် တွေ့ထားသည်"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"အကောင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"အကောင့်တွင် အန္တရာယ်ရှိသည်"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{သတိပေးချက် # ခု}other{သတိပေးချက် # ခု}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-nb/strings.xml b/SafetyCenter/Resources/shared_res/values-nb/strings.xml index 12ac898d0..2bd116b8d 100644 --- a/SafetyCenter/Resources/shared_res/values-nb/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-nb/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sjekk innstillingslisten"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheten kan være i faresonen"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheten er i faresonen"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du kan være i faresonen"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du er i faresonen"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dataene dine kan være i faresonen"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dataene dine er i faresonen"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontoen kan være i faresonen"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontoen er i faresonen"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# varsel}other{# varsler}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ne/strings.xml b/SafetyCenter/Resources/shared_res/values-ne/strings.xml index f89da5c4f..7e2cdd297 100644 --- a/SafetyCenter/Resources/shared_res/values-ne/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ne/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिङको सूची जाँच्नुहोस्"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिभाइस जोखिममा हुन सक्छ"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिभाइस जोखिममा छ"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"सुरक्षा र गोपनीयता जोखिममा हुन सक्छ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"सुरक्षा र गोपनीयता जोखिममा छ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"तपाईंको डेटा जोखिममा हुन सक्छ"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"तपाईंको डेटा जोखिममा छ"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"पासवर्ड ह्याक भएको छ (पुरानो)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"पासवर्ड ह्याक भएको छ (नयाँ पासवर्ड)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"तपाईंको सुरक्षा जोखिममा हुन सक्छ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"तपाईंको खाता जोखिममा छ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"सम्भावित जोखिम फेला परे"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"जोखिम फेला परे"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाता जोखिममा हुन सक्छ"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाता जोखिममा छ"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# अलर्ट}other{# वटा अलर्ट}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-nl/strings.xml b/SafetyCenter/Resources/shared_res/values-nl/strings.xml index ef6041683..4ca888201 100644 --- a/SafetyCenter/Resources/shared_res/values-nl/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-nl/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check de lijst met instellingen"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Apparaat loopt misschien gevaar"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Apparaat loopt gevaar"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Je loopt misschien risico"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Je loopt risico"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Je gegevens lopen misschien gevaar"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Je gegevens lopen risico"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account loopt misschien gevaar"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account loopt risico"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# melding}other{# meldingen}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-or/strings.xml b/SafetyCenter/Resources/shared_res/values-or/strings.xml index 30a7f15f1..574d5f7e3 100644 --- a/SafetyCenter/Resources/shared_res/values-or/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-or/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ସେଟିଂସ ତାଲିକା ଯାଞ୍ଚ କରନ୍ତୁ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ଡିଭାଇସଟି ରିସ୍କରେ ଥାଇପାରେ"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ଡିଭାଇସଟି ରିସ୍କରେ ଅଛି"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ଆପଣ ବିପଦରେ ଥାଇପାରନ୍ତି"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ଆପଣ ବିପଦରେ ଅଛନ୍ତି"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ଆପଣଙ୍କ ଡାଟା ବିପଦରେ ଥାଇପାରେ"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ଆପଣଙ୍କ ଡାଟା ବିପଦରେ ଅଛି"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ପାସୱାର୍ଡ ଚୋରି ହୋଇଯାଇଛି (ପୁରୁଣା)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ପାସୱାର୍ଡ ଚୋରି ହୋଇଯାଇଛି (ନୂଆ)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ଆପଣ ବିପଦରେ ଥାଇପାରନ୍ତି"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ଆପଣ ବିପଦରେ ଅଛନ୍ତି"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ସମ୍ଭାବ୍ୟ ବିପଦ ମିଳିଛି"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ବିପଦ ମିଳିଛି"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ଆକାଉଣ୍ଟ ବିପଦରେ ଥାଇପାରେ"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ଆକାଉଣ୍ଟ ବିପଦରେ ଅଛି"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{#ଟି ଆଲର୍ଟ}other{#ଟି ଆଲର୍ଟ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-pa/strings.xml b/SafetyCenter/Resources/shared_res/values-pa/strings.xml index b87a7efeb..55cc2adb7 100644 --- a/SafetyCenter/Resources/shared_res/values-pa/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-pa/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ਸੈਟਿੰਗਾਂ ਸੂਚੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ਡੀਵਾਈਸ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ਡੀਵਾਈਸ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦੀ ਹੈ"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ਤੁਹਾਡਾ ਡਾਟਾ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ਤੁਹਾਡਾ ਡਾਟਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ਪਾਸਵਰਡਾਂ ਨਾਲ ਛੇੜਛਾੜ ਹੋਈ (ਪੁਰਾਣਾ)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ਪਾਸਵਰਡਾਂ ਨਾਲ ਛੇੜਛਾੜ ਹੋਈ (ਨਵਾਂ)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦੀ ਹੈ"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ਸੰਭਾਵੀ ਜੋਖਮਾਂ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ਜੋਖਮਾਂ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ਸੁਚੇਤਨਾ}one{# ਸੁਚੇਤਨਾ}other{# ਸੁਚੇਤਨਾਵਾਂ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-pl/strings.xml b/SafetyCenter/Resources/shared_res/values-pl/strings.xml index 7a7148bdf..d3838c9d1 100644 --- a/SafetyCenter/Resources/shared_res/values-pl/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-pl/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sprawdź listę ustawień"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Urządzenie może być zagrożone"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Urządzenie jest zagrożone"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Twoje bezpieczeństwo może być zagrożone"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Twoje bezpieczeństwo jest zagrożone"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dane mogą być zagrożone"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dane są zagrożone"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Przejęte hasła (stare)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Przejęte hasła (nowe)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Twoje bezpieczeństwo może być zagrożone"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Twoje bezpieczeństwo jest zagrożone"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Wykryte potencjalne zagrożenia"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Wykryte zagrożenia"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto może być zagrożone"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto jest zagrożone"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}few{# alerty}many{# alertów}other{# alertu}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml index 721309dd1..e2d4423e6 100644 --- a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verificar lista de configurações"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Você pode estar em risco"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Você está em risco"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Seus dados podem estar em risco"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Seus dados estão em risco"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Senhas comprometidas (antigas)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Senhas comprometidas (novas)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Você pode estar em risco"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Você está em risco"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Possíveis riscos encontrados"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}one{# alerta}many{# de alertas}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml index b7b917173..329fb9ebf 100644 --- a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifique a lista de definições"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pode estar em risco"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Está em risco"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Os seus dados podem estar em risco"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Os seus dados estão em risco"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Pal.-passe antigas comprometidas"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Palavras-passe novas comprometidas"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Pode estar em risco"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Está em risco"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potenciais riscos encontrados"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-pt/strings.xml b/SafetyCenter/Resources/shared_res/values-pt/strings.xml index 721309dd1..e2d4423e6 100644 --- a/SafetyCenter/Resources/shared_res/values-pt/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-pt/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verificar lista de configurações"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Você pode estar em risco"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Você está em risco"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Seus dados podem estar em risco"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Seus dados estão em risco"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Senhas comprometidas (antigas)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Senhas comprometidas (novas)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Você pode estar em risco"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Você está em risco"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Possíveis riscos encontrados"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}one{# alerta}many{# de alertas}other{# alertas}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ro/strings.xml b/SafetyCenter/Resources/shared_res/values-ro/strings.xml index 43b705382..21fc3dddc 100644 --- a/SafetyCenter/Resources/shared_res/values-ro/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ro/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifică lista de setări"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Dispozitivul poate fi expus la risc"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Dispozitivul este expus riscului"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"E posibil să fii expus(ă) la risc"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ești expus(ă) la risc"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Datele tale pot fi în pericol"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datele tale sunt în pericol"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parole compromise (vechi)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parole compromise (noi)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"E posibil să fii expus(ă) la risc"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ești expus(ă) la risc"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"S-au găsit riscuri potențiale"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscuri descoperite"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Contul poate fi în pericol"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Contul este în pericol"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alertă}few{# alerte}other{# de alerte}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ru/strings.xml b/SafetyCenter/Resources/shared_res/values-ru/strings.xml index b187ecfb6..cdf68fd97 100644 --- a/SafetyCenter/Resources/shared_res/values-ru/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ru/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Изучите список настроек"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Устройство может быть под угрозой"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Устройство под угрозой"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Безопасность может быть под угрозой"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ваша безопасность под угрозой"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Ваши данные могут быть под угрозой"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ваши данные под угрозой"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Раскрыты старые пароли"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Раскрыты новые пароли"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Безопасность может быть под угрозой"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ваша безопасность под угрозой"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Обнаружены потенциальные риски"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Обнаружены риски"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунт может быть под угрозой"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунт под угрозой"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# оповещение}one{# оповещение}few{# оповещения}many{# оповещений}other{# оповещения}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-si/strings.xml b/SafetyCenter/Resources/shared_res/values-si/strings.xml index 4fe648370..80ecaa301 100644 --- a/SafetyCenter/Resources/shared_res/values-si/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-si/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"සැකසීම් ලැයිස්තුව පරීක්ෂා කරන්න"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"උපාංගය අවදානමේ තිබිය හැක"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"උපාංගය අවදානමේ ඇත"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ඔබ අවදානමේ සිටිය හැක"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ඔබ අවදානමේ සිටියි"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ඔබේ දත්ත අවදානමට ලක් විය හැක"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ඔබේ දත්ත අවදානමේ ඇත"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ගිණුම අවදානමේ තිබිය හැක"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ගිණුම අවදානමේ පවතී"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ඇඟවීමක්}one{ඇඟවීම් #ක්}other{ඇඟවීම් #ක්}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sk/strings.xml b/SafetyCenter/Resources/shared_res/values-sk/strings.xml index 02c57e952..43166edfb 100644 --- a/SafetyCenter/Resources/shared_res/values-sk/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sk/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrolný zoznam nastavení"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Zariadenie môže byť ohrozené"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Zariadenie je ohrozené"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vaše zabezpečenie môže byť ohrozené"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vaše zabezpečenie je ohrozené"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vaše údaje môžu byť ohrozené"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vaše údaje sú ohrozené"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Účet môže byť ohrozený"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Účet je ohrozený"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozornenie}few{# upozornenia}many{# alerts}other{# upozornení}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sl/strings.xml b/SafetyCenter/Resources/shared_res/values-sl/strings.xml index b4c820896..7ce5e01af 100644 --- a/SafetyCenter/Resources/shared_res/values-sl/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sl/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Seznam za preverjanje nastavitev"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Naprava je morda ogrožena"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Naprava je ogrožena"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Morda ste ogroženi"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ste ogroženi"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podatki so morda ogroženi"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podatki so ogroženi"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Gesla so ogrožena (stara)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Gesla so ogrožena (nova)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Morda ste ogroženi"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ste ogroženi"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Najdena so morebitna tveganja"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Najdena so tveganja"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun je morda ogrožen"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je ogrožen"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# opozorilo}one{# opozorilo}two{# opozorili}few{# opozorila}other{# opozoril}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sq/strings.xml b/SafetyCenter/Resources/shared_res/values-sq/strings.xml index 77263fd0d..9d40b8677 100644 --- a/SafetyCenter/Resources/shared_res/values-sq/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sq/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollo listën e cilësimeve"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Pajisja mund të jetë në rrezik"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Pajisja është në rrezik"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Mund të jesh në rrezik"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Je në rrezik"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Të dhënat e tua mund të jenë në rrezik"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Të dhënat e tua janë në rrezik"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Llogaria mund të jetë në rrezik"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Llogaria është në rrezik"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# sinjalizim}other{# sinjalizime}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sr/strings.xml b/SafetyCenter/Resources/shared_res/values-sr/strings.xml index e46e4f970..9bd52b9cf 100644 --- a/SafetyCenter/Resources/shared_res/values-sr/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sr/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверите листу подешавања"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Уређај је можда угрожен"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Уређај је угрожен"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можда сте угрожени"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Угрожени сте"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Подаци су можда угрожени"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Подаци су угрожени"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Лозинке су угрожене (старе)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Лозинке су угрожене (нове)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Можда сте угрожени"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Угрожени сте"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Пронађени су потенцијални ризици"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Пронађени су ризици"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Налог је можда угрожен"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Налог је угрожен"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# обавештење}one{# обавештење}few{# обавештења}other{# обавештења}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sv/strings.xml b/SafetyCenter/Resources/shared_res/values-sv/strings.xml index cd3489950..5cf5f3455 100644 --- a/SafetyCenter/Resources/shared_res/values-sv/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sv/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollera listan med inställningar"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheten kan vara i fara"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheten är i fara"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du kan vara utsatt för fara"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du är i fara"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Din data kan vara i fara"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Din data är i fara"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Utsatta lösenord (gamla)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Utsatta lösenord (nya)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Du kan vara i riskzonen"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Du är i riskzonen"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potentiella risker har upptäckts"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risker har upptäckts"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontot kan vara i fara"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontot är i fara"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# varning}other{# varningar}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-sw/strings.xml b/SafetyCenter/Resources/shared_res/values-sw/strings.xml index 3226a3a10..6dd541894 100644 --- a/SafetyCenter/Resources/shared_res/values-sw/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-sw/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kagua orodha ya mipangilio"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Huenda kifaa kikawa hatarini"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Kifaa kiko hatarini"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Huenda uko hatarini"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Uko hatarini"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Huenda data yako iko hatarini"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data yako iko hatarini"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Huenda akaunti iko hatarini"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akaunti iko hatarini"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{Arifa #}other{Arifa #}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ta/strings.xml b/SafetyCenter/Resources/shared_res/values-ta/strings.xml index d8693c872..a534d0eaf 100644 --- a/SafetyCenter/Resources/shared_res/values-ta/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ta/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"அமைப்புகள் பட்டியலைச் சரிபாருங்கள்"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"சாதனம் ஆபத்தில் இருக்கக்கூடும்"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"சாதனம் ஆபத்தில் உள்ளது"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"நீங்கள் ஆபத்தில் இருக்கலாம்"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"நீங்கள் ஆபத்தில் இருக்கிறீர்கள்"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"உங்கள் தரவு ஆபத்தில் இருக்கலாம்"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"உங்கள் தரவு ஆபத்தில் உள்ளது"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"கடவுச்சொற்கள் களவாடப்பட்டுள்ளன (பழையவை)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"கடவுச்சொற்கள் களவாடப்பட்டுள்ளன (புதியவை)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"நீங்கள் ஆபத்தில் இருக்கலாம்"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"நீங்கள் ஆபத்தில் இருக்கிறீர்கள்"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"சாத்தியமுள்ள ஆபத்து கண்டறியப்பட்டன"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ஆபத்துகள் கண்டறியப்பட்டுள்ளன"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"கணக்கு ஆபத்தில் இருக்கலாம்"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"கணக்கு ஆபத்தில் உள்ளது"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# எச்சரிக்கை}other{# எச்சரிக்கைகள்}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-te/strings.xml b/SafetyCenter/Resources/shared_res/values-te/strings.xml index 9eb6341fe..98bbf9c31 100644 --- a/SafetyCenter/Resources/shared_res/values-te/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-te/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"సెట్టింగ్ల లిస్ట్ను చెక్ చేయండి"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"పరికరం ప్రమాదంలో ఉండవచ్చు"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"పరికరం ప్రమాదంలో ఉంది"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"మీరు రిస్క్లో ఉండవచ్చు"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"మీరు రిస్క్లో ఉన్నారు"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"మీ డేటా ప్రమాదంలో ఉండవచ్చు"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"మీ డేటా ప్రమాదంలో ఉంది"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"(పాత) సురక్షితం కాని పాస్వర్డ్స్"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"(కొత్త) సురక్షితం కాని పాస్వర్డ్స్"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"మీరు రిస్క్లో ఉండవచ్చు"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"మీరు రిస్క్లో ఉన్నారు"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"అవకాశం ఉన్న రిస్క్లు కనుగొనబడ్డాయి"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"రిస్క్లు కనుగొనబడ్డాయి"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ఖాతా ప్రమాదంలో ఉండవచ్చు"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ఖాతా ప్రమాదంలో ఉంది"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# అలర్ట్}other{# అలర్ట్లు}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-th/strings.xml b/SafetyCenter/Resources/shared_res/values-th/strings.xml index e457d5ef6..c627090cb 100644 --- a/SafetyCenter/Resources/shared_res/values-th/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-th/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ตรวจสอบรายการการตั้งค่า"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"อุปกรณ์อาจมีความเสี่ยง"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"อุปกรณ์มีความเสี่ยง"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"คุณอาจมีความเสี่ยง"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"คุณกำลังมีความเสี่ยง"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ข้อมูลของคุณอาจมีความเสี่ยง"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ข้อมูลของคุณมีความเสี่ยง"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"รหัสผ่าน (เก่า) ถูกละเมิด"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"รหัสผ่าน (ใหม่) ถูกละเมิด"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"คุณอาจมีความเสี่ยง"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"คุณกำลังมีความเสี่ยง"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"พบความเสี่ยงที่อาจเกิดขึ้น"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"พบความเสี่ยง"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"บัญชีอาจมีความเสี่ยง"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"บัญชีมีความเสี่ยง"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-tl/strings.xml b/SafetyCenter/Resources/shared_res/values-tl/strings.xml index 21372a1a8..12ab86d86 100644 --- a/SafetyCenter/Resources/shared_res/values-tl/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-tl/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tingnan ang listahan ng mga setting"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Posibleng nanganganib ang device"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Nanganganib ang device"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Posibleng nanganganib ka"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Nanganganib ka"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Posibleng nanganganib ang data mo"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Nanganganib ang iyong data"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Nakompromiso ang password (luma)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Nakompromiso ang password (bago)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Posibleng nanganganib ka"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Nanganganib ka"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"May nakitang potensyal na panganib"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"May nakitang mga panganib"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Posibleng nanganganib ang account"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Nanganganib ang account"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerto}one{# alerto}other{# na alerto}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-tr/strings.xml b/SafetyCenter/Resources/shared_res/values-tr/strings.xml index d340bccf1..3656c75d4 100644 --- a/SafetyCenter/Resources/shared_res/values-tr/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-tr/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ayarlar listesini kontrol edin"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Cihaz risk altında olabilir"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Cihaz risk altında"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risk altında olabilirsiniz"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risk altındasınız"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Verileriniz risk altında olabilir"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Verileriniz risk altında"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Şifrelerin güvenliği ihlal edildi (eski)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Şifrelerin güvenliği ihlal edildi (yeni)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risk altında olabilirsiniz"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risk altındasınız"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Olası riskler bulundu"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risk bulundu"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hesap risk altında olabilir"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hesap risk altında"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# uyarı}other{# uyarı}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-uk/strings.xml b/SafetyCenter/Resources/shared_res/values-uk/strings.xml index 369d5943f..09363cc30 100644 --- a/SafetyCenter/Resources/shared_res/values-uk/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-uk/strings.xml @@ -25,8 +25,20 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Перегляньте список налаштувань"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Можливо, пристрій під загрозою"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Пристрій під загрозою"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можливо, ваша безпека під загрозою"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ваша безпека під загрозою"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Можлива загроза даним"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ваші дані під загрозою"</string> + <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) --> + <skip /> + <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) --> + <skip /> + <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) --> + <skip /> + <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) --> + <skip /> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Можлива загроза обліковому запису"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Обліковий запис під загрозою"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-ur/strings.xml b/SafetyCenter/Resources/shared_res/values-ur/strings.xml index 85734b28b..e6edf41a2 100644 --- a/SafetyCenter/Resources/shared_res/values-ur/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-ur/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ترتیبات کی فہرست چیک کریں"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"آلہ خطرے میں ہو سکتا ہے"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"آلہ خطرے میں ہے"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"آپ خطرے میں ہو سکتے ہیں"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"آپ خطرے میں ہے"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"آپ کا ڈیٹا خطرے میں ہو سکتا ہے"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"آپ کا ڈیٹا خطرے میں ہے"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"متاثرہ پاس ورڈز (پرانا)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"متاثرہ پاس ورڈز (نیا)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"آپ خطرے میں ہو سکتے ہیں"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"آپ خطرے میں ہے"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ممکنہ خطرات پائے گئے"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"خطرات پائے گئے"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"اکاؤنٹ خطرے میں ہو سکتا ہے"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"اکاؤنٹ خطرے میں ہے"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# الرٹ}other{# الرٹس}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-uz/strings.xml b/SafetyCenter/Resources/shared_res/values-uz/strings.xml index 8495f0ae8..3995f4995 100644 --- a/SafetyCenter/Resources/shared_res/values-uz/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-uz/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sozlamalar roʻyxatini tekshirish"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Qurilma xavf ostida boʻlishi mumkin"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Qurilma xavf ostida"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Xavf ostida boʻlishingiz mumkin"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Xavf ostidasiz"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Maʼlumotlar xavf ostida shekilli"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Maʼlumotlaringiz xavf ostida"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parollar oshkor qilindi (eski)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parollar oshkor qilindi (yangi)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Xavf ostida boʻlishingiz mumkin"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Xavf ostidasiz"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potentsial xavflar topildi"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Xavflar topildi"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hisob xavf ostida shekilli"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hisob xavf ostida"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ta ogohlantirish}other{# ta ogohlantirish}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-vi/strings.xml b/SafetyCenter/Resources/shared_res/values-vi/strings.xml index 8662a142f..f04eb0640 100644 --- a/SafetyCenter/Resources/shared_res/values-vi/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-vi/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kiểm tra danh sách cài đặt"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Thiết bị có thể gặp rủi ro"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Thiết bị đang gặp rủi ro"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Bạn có thể gặp rủi ro"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Bạn đang gặp rủi ro"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dữ liệu của bạn có thể gặp rủi ro"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dữ liệu của bạn đang gặp rủi ro"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mật khẩu bị lộ (mật khẩu cũ)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mật khẩu bị lộ (mật khẩu mới)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Bạn có thể gặp rủi ro"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Bạn đang gặp rủi ro"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Đã phát hiện rủi ro tiềm ẩn"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Đã phát hiện rủi ro"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Tài khoản có thể gặp rủi ro"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Tài khoản đang gặp rủi ro"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# cảnh báo}other{# cảnh báo}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml index b467b3258..da7395d1f 100644 --- a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"请检查设置列表"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"设备可能存在风险"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"设备存在风险"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"您可能有风险"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"您目前有风险"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"您的数据可能存在风险"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"您的数据存在风险"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"密码被盗(旧密码)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"密码被盗(新密码)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"您可能有风险"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"您目前有风险"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"发现潜在风险"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"发现风险"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帐号可能存在风险"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"帐号存在风险"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 条提醒}other{# 条提醒}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml index 6c99e38a2..420c5ddc5 100644 --- a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"查看設定清單"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"裝置可能存在風險"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"裝置存在風險"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"您可能面臨風險"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"您正面臨風險"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"您的資料可能面臨風險"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"您的資料正面臨風險"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"被盜用的密碼 (舊)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"被盜用的密碼 (新)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"您可能面臨風險"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"您正面臨風險"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"系統發現潛在風險"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"系統發現風險"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帳戶可能面臨風險"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"裝置正面臨風險"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 個警示}other{# 個警示}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml index ad9f255ed..4bc295695 100644 --- a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"檢查設定清單"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"裝置可能有風險"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"裝置有風險"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"你可能有風險"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"你目前有風險"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"你的資料可能面臨風險"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"你的資料目前有風險"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"舊密碼已遭外洩"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"新密碼已遭外洩"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"你的帳戶可能有風險"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"你的帳戶有風險"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"發現潛在風險"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"發現風險"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帳戶可能面臨風險"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"帳戶目前有風險"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 個警示}other{# 個警示}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values-zu/strings.xml b/SafetyCenter/Resources/shared_res/values-zu/strings.xml index fb23e9e35..637577a5e 100644 --- a/SafetyCenter/Resources/shared_res/values-zu/strings.xml +++ b/SafetyCenter/Resources/shared_res/values-zu/strings.xml @@ -25,8 +25,14 @@ <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Hlola uhlu lwamasethingi"</string> <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Idivayisi ingaba sengozini"</string> <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Idivayisi isengozini"</string> - <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Ungaba sengozini"</string> - <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Usengozini"</string> + <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Idatha yakho isengozini"</string> + <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Idatha yakho isengozini"</string> + <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Amaphasiwedi onakalisiwe (amadala)"</string> + <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Amaphasiwedi onakalisiwe (amasha)"</string> + <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Kungenzeka usengozini"</string> + <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Usengozini"</string> + <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Izingozi ezingaba khona zitholakele"</string> + <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Izingozi ezitholakele"</string> <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"I-akhawunti ingaba sengozini"</string> <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"I-akhawunti isengcupheni"</string> <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{Isexwayiso esi-#}one{Izexwayiso ezingu-#}other{Izexwayiso ezingu-#}}"</string> diff --git a/SafetyCenter/Resources/shared_res/values/strings.xml b/SafetyCenter/Resources/shared_res/values/strings.xml index 42b91e012..964812e6d 100644 --- a/SafetyCenter/Resources/shared_res/values/strings.xml +++ b/SafetyCenter/Resources/shared_res/values/strings.xml @@ -40,11 +40,29 @@ <!-- Title for the overall Safety Center status when the user security and privacy signals are putting them at risk [CHAR LIMIT=35] --> <string name="overall_severity_level_critical_device_warning_title">Device is at risk</string> + <!-- Title for the overall Safety Center status when the user's data could potentially be at risk [CHAR LIMIT=35] --> + <string name="overall_severity_level_data_recommendation_title">Your data may be at risk</string> + + <!-- Title for the overall Safety Center status when the user's data is at risk [CHAR LIMIT=35] --> + <string name="overall_severity_level_critical_data_warning_title">Your data is at risk</string> + + <!-- Title for the overall Safety Center status when the user's passwords could potentially be at risk (old or unused passwords) [CHAR LIMIT=35] --> + <string name="overall_severity_level_passwords_recommendation_title">Passwords compromised (old)</string> + + <!-- Title for the overall Safety Center status when the user's passwords are at risk (new passwords) [CHAR LIMIT=35] --> + <string name="overall_severity_level_critical_passwords_warning_title">Passwords compromised (new)</string> + + <!-- Title for the overall Safety Center status when the user's personal safety could potentially be at risk [CHAR LIMIT=35] --> + <string name="overall_severity_level_personal_recommendation_title">You may be at risk</string> + + <!-- Title for the overall Safety Center status when the user's personal safety is at risk [CHAR LIMIT=35] --> + <string name="overall_severity_level_critical_personal_warning_title">You are at risk</string> + <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their general safety at risk [CHAR LIMIT=35] --> - <string name="overall_severity_level_safety_recommendation_title">You may be at risk</string> + <string name="overall_severity_level_safety_recommendation_title">Potential risks found</string> <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their general safety at risk [CHAR LIMIT=35] --> - <string name="overall_severity_level_critical_safety_warning_title">You are at risk</string> + <string name="overall_severity_level_critical_safety_warning_title">Risks found</string> <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their account at risk [CHAR LIMIT=35] --> <string name="overall_severity_level_account_recommendation_title">Account may be at risk</string> diff --git a/SafetyCenter/TEST_MAPPING b/SafetyCenter/TEST_MAPPING index e2eb5ef5d..c702ee852 100644 --- a/SafetyCenter/TEST_MAPPING +++ b/SafetyCenter/TEST_MAPPING @@ -24,5 +24,15 @@ { "name": "SafetyCenterFunctionalTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/SafetyLabel/Android.bp b/SafetyLabel/Android.bp new file mode 100644 index 000000000..119890d3f --- /dev/null +++ b/SafetyLabel/Android.bp @@ -0,0 +1,49 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "safety-label-java-sources", + srcs: [ + "java/**/*.java", + ], + path: "java", + visibility: ["//visibility:private"], +} + +java_library { + name: "safety-label", + sdk_version: "system_current", + min_sdk_version: "30", + target_sdk_version: "33", + srcs: [ + ":safety-label-java-sources", + ], + libs: [ + "androidx.annotation_annotation", + "framework-annotations-lib", + ], + apex_available: [ + "com.android.permission", + "test_com.android.permission", + ], + installable: false, + visibility: [ + "//packages/modules/Permission:__subpackages__", + ], +} + diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java b/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java new file mode 100644 index 000000000..395468c81 --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java @@ -0,0 +1,100 @@ +/* + * 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.permission.safetylabel; + +import android.os.PersistableBundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.permission.safetylabel.DataLabelConstants.DataUsage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Data usage category representation containing one or more {@link DataType}. Valid category keys + * are defined in {@link DataCategoryConstants}, each category has a valid set of types {@link + * DataType}, which are mapped in {@link DataTypeConstants} + */ +public class DataCategory { + private final Map<String, DataType> mDataTypes; + + private DataCategory(@NonNull Map<String, DataType> dataTypes) { + this.mDataTypes = dataTypes; + } + + /** + * Returns a {@link java.util.Collections.UnmodifiableMap} of {@link String} category to {@link + * DataCategory} created by parsing a {@link PersistableBundle} + */ + @NonNull + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + static Map<String, DataCategory> getDataCategoryMap( + @Nullable PersistableBundle dataLabelBundle, @DataUsage @NonNull String dataUsage) { + if (dataLabelBundle == null) { + return Collections.emptyMap(); + } + + PersistableBundle dataCategoryMapBundle = dataLabelBundle.getPersistableBundle(dataUsage); + if (dataCategoryMapBundle == null) { + return Collections.emptyMap(); + } + + Map<String, DataCategory> dataCategoryMap = new HashMap<>(); + for (String category : DataCategoryConstants.VALID_CATEGORIES) { + DataCategory dataCategory = getDataCategory(dataCategoryMapBundle, dataUsage, category); + if (dataCategory != null) { + dataCategoryMap.put(category, dataCategory); + } + } + return Collections.unmodifiableMap(dataCategoryMap); + } + + /** + * Returns a {@link DataCategory} created by parsing a {@link PersistableBundle}, or {@code + * null} if parsing results in an invalid or empty DataCategory + */ + @Nullable + @VisibleForTesting + static DataCategory getDataCategory( + @Nullable PersistableBundle dataCategoryMapBundle, + @NonNull String dataUsage, + @NonNull String category) { + if (dataCategoryMapBundle == null) { + return null; + } + + PersistableBundle dataCategoryBundle = dataCategoryMapBundle.getPersistableBundle(category); + + Map<String, DataType> dataTypeMap = + DataType.getDataTypeMap(dataCategoryBundle, dataUsage, category); + if (dataTypeMap.isEmpty()) { + return null; + } + + return new DataCategory(dataTypeMap); + } + + /** Return the type {@link Map} of String type key to {@link DataType} */ + @NonNull + public Map<String, DataType> getDataTypes() { + return mDataTypes; + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java new file mode 100644 index 000000000..af04220c0 --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java @@ -0,0 +1,101 @@ +/* + * 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.permission.safetylabel; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Constants for determining valid {@link String} data types for usage within {@link SafetyLabel}, + * {@link DataCategory}, and {@link DataType} + */ +public class DataCategoryConstants { + + /** List of valid Safety Label data collection/sharing categories */ + @Retention(SOURCE) + @StringDef( + prefix = "CATEGORY_", + value = { + CATEGORY_PERSONAL, + CATEGORY_FINANCIAL, + CATEGORY_LOCATION, + CATEGORY_EMAIL_TEXT_MESSAGE, + CATEGORY_PHOTO_VIDEO, + CATEGORY_AUDIO, + CATEGORY_STORAGE, + CATEGORY_HEALTH_FITNESS, + CATEGORY_CONTACTS, + CATEGORY_CALENDAR, + CATEGORY_IDENTIFIERS, + CATEGORY_APP_PERFORMANCE, + CATEGORY_ACTIONS_IN_APP, + CATEGORY_SEARCH_AND_BROWSING, + }) + public @interface Category {} + + public static final String CATEGORY_PERSONAL = "personal"; + public static final String CATEGORY_FINANCIAL = "financial"; + public static final String CATEGORY_LOCATION = "location"; + public static final String CATEGORY_EMAIL_TEXT_MESSAGE = "email_text_message"; + public static final String CATEGORY_PHOTO_VIDEO = "photo_video"; + public static final String CATEGORY_AUDIO = "audio"; + public static final String CATEGORY_STORAGE = "storage"; + public static final String CATEGORY_HEALTH_FITNESS = "health_fitness"; + public static final String CATEGORY_CONTACTS = "contacts"; + public static final String CATEGORY_CALENDAR = "calendar"; + public static final String CATEGORY_IDENTIFIERS = "identifiers"; + public static final String CATEGORY_APP_PERFORMANCE = "app_performance"; + public static final String CATEGORY_ACTIONS_IN_APP = "actions_in_app"; + public static final String CATEGORY_SEARCH_AND_BROWSING = "search_and_browsing"; + + /** Set of valid categories */ + @DataCategoryConstants.Category + public static final Set<String> VALID_CATEGORIES = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + CATEGORY_PERSONAL, + CATEGORY_FINANCIAL, + CATEGORY_LOCATION, + CATEGORY_EMAIL_TEXT_MESSAGE, + CATEGORY_PHOTO_VIDEO, + CATEGORY_AUDIO, + CATEGORY_STORAGE, + CATEGORY_HEALTH_FITNESS, + CATEGORY_CONTACTS, + CATEGORY_CALENDAR, + CATEGORY_IDENTIFIERS, + CATEGORY_APP_PERFORMANCE, + CATEGORY_ACTIONS_IN_APP, + CATEGORY_SEARCH_AND_BROWSING))); + + /** Returns {@link Set} of valid {@link String} category keys */ + public static Set<String> getValidDataCategories() { + return VALID_CATEGORIES; + } + + private DataCategoryConstants() { + /* do nothing - hide constructor */ + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java new file mode 100644 index 000000000..564d5479f --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java @@ -0,0 +1,83 @@ +/* + * 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.permission.safetylabel; + +import static com.android.permission.safetylabel.DataLabelConstants.DATA_USAGE_COLLECTED; +import static com.android.permission.safetylabel.DataLabelConstants.DATA_USAGE_SHARED; + +import android.os.PersistableBundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import java.util.Map; + +/** + * Data label representation with data shared and data collected maps containing zero or more + * {@link DataCategory} + */ +public class DataLabel { + @VisibleForTesting static final String KEY_DATA_LABEL = "data_labels"; + private final Map<String, DataCategory> mDataCollected; + private final Map<String, DataCategory> mDataShared; + + public DataLabel( + @NonNull Map<String, DataCategory> dataCollected, + @NonNull Map<String, DataCategory> dataShared) { + mDataCollected = dataCollected; + mDataShared = dataShared; + } + + /** Returns a {@link DataLabel} created by parsing a SafetyLabel {@link PersistableBundle} */ + @NonNull + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public static DataLabel getDataLabel(@Nullable PersistableBundle safetyLabelBundle) { + if (safetyLabelBundle == null) { + return null; + } + + PersistableBundle dataLabelBundle = safetyLabelBundle.getPersistableBundle(KEY_DATA_LABEL); + if (dataLabelBundle == null) { + return null; + } + + Map<String, DataCategory> dataCollectedCategoryMap = + DataCategory.getDataCategoryMap(dataLabelBundle, DATA_USAGE_COLLECTED); + Map<String, DataCategory> dataSharedCategoryMap = + DataCategory.getDataCategoryMap(dataLabelBundle, DATA_USAGE_SHARED); + return new DataLabel(dataCollectedCategoryMap, dataSharedCategoryMap); + } + + /** + * Returns the data collected {@link Map} of {@link + * com.android.permission.safetylabel.DataCategoryConstants.Category} to {@link DataCategory} + */ + @NonNull + public Map<String, DataCategory> getDataCollected() { + return mDataCollected; + } + + /** + * Returns the data shared {@link Map} of {@link + * com.android.permission.safetylabel.DataCategoryConstants.Category} to {@link DataCategory} + */ + @NonNull + public Map<String, DataCategory> getDataShared() { + return mDataShared; + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java new file mode 100644 index 000000000..f592d9b0f --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java @@ -0,0 +1,42 @@ +/* + * 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.permission.safetylabel; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.StringDef; + +import java.lang.annotation.Retention; + +/** + * Constants and util methods for determining valid {@link String} data types for usage within + * {@link SafetyLabel} and {@link DataLabel} + */ +public class DataLabelConstants { + + /** List of valid Safety Label data usages. Shared vs Collected */ + @Retention(SOURCE) + @StringDef( + prefix = "DATA_USAGE_", + value = { + DATA_USAGE_COLLECTED, + DATA_USAGE_SHARED, + }) + public @interface DataUsage {} + public static final String DATA_USAGE_COLLECTED = "data_collected"; + public static final String DATA_USAGE_SHARED = "data_shared"; +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java new file mode 100644 index 000000000..0e1bb31ce --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java @@ -0,0 +1,79 @@ +/* + * 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.permission.safetylabel; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Constants for determining valid {@link Integer} data usage purposes for usage within + * {@link DataType} + */ +public class DataPurposeConstants { + + /** List of valid data usage purposes */ + @Retention(SOURCE) + @IntDef( + prefix = "PURPOSE_", + value = { + PURPOSE_APP_FUNCTIONALITY, + PURPOSE_ANALYTICS, + PURPOSE_DEVELOPER_COMMUNICATIONS, + PURPOSE_FRAUD_PREVENTION_SECURITY, + PURPOSE_ADVERTISING, + PURPOSE_PERSONALIZATION, + PURPOSE_ACCOUNT_MANAGEMENT, + }) + public @interface Purpose {} + + public static final int PURPOSE_APP_FUNCTIONALITY = 1; + public static final int PURPOSE_ANALYTICS = 2; + public static final int PURPOSE_DEVELOPER_COMMUNICATIONS = 3; + public static final int PURPOSE_FRAUD_PREVENTION_SECURITY = 4; + public static final int PURPOSE_ADVERTISING = 5; + public static final int PURPOSE_PERSONALIZATION = 6; + public static final int PURPOSE_ACCOUNT_MANAGEMENT = 7; + // RESERVED/DEPRECATED = 8 + // RESERVED/DEPRECATED = 9 + + /** {@link Set} of valid {@link Integer} purposes */ + @Purpose + public static final Set<Integer> VALID_PURPOSES = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + PURPOSE_APP_FUNCTIONALITY, + PURPOSE_ANALYTICS, + PURPOSE_DEVELOPER_COMMUNICATIONS, + PURPOSE_FRAUD_PREVENTION_SECURITY, + PURPOSE_ADVERTISING, + PURPOSE_PERSONALIZATION, + PURPOSE_ACCOUNT_MANAGEMENT))); + + /** Returns {@link Set} of valid {@link Integer} purpose */ + @Purpose + public static Set<Integer> getValidPurposes() { + return VALID_PURPOSES; + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataType.java b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java new file mode 100644 index 000000000..cc63b07ac --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java @@ -0,0 +1,154 @@ +/* + * 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.permission.safetylabel; + +import android.os.PersistableBundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.permission.safetylabel.DataPurposeConstants.Purpose; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Data usage type representation. Types are specific to a {@link DataCategory} and contains + * metadata related to the data usage purpose. + */ +public class DataType { + @VisibleForTesting static final String KEY_PURPOSES = "purposes"; + @VisibleForTesting static final String KEY_USER_CONTROL = "user_control"; + @VisibleForTesting static final String KEY_EPHEMERAL = "ephemeral"; + + @Purpose private final Set<Integer> mPurposeSet; + private final Boolean mUserControl; + private final Boolean mEphemeral; + + private DataType( + @NonNull @Purpose Set<Integer> purposeSet, + @Nullable Boolean userControl, + @Nullable Boolean ephemeral) { + this.mPurposeSet = purposeSet; + this.mUserControl = userControl; + this.mEphemeral = ephemeral; + } + + /** + * Returns a {@link java.util.Collections.UnmodifiableMap} of String type key to {@link + * DataType} created by parsing a {@link PersistableBundle} + */ + @NonNull + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + static Map<String, DataType> getDataTypeMap( + @Nullable PersistableBundle dataCategoryBundle, + @NonNull String dataUsage, + @NonNull String category) { + if (dataCategoryBundle == null || dataCategoryBundle.isEmpty()) { + return Collections.emptyMap(); + } + + Map<String, DataType> dataTypeMap = new HashMap<>(); + Set<String> validDataTypesForCategory = + DataTypeConstants.getValidDataTypesForCategory(category); + for (String type : validDataTypesForCategory) { + PersistableBundle dataTypeBundle = dataCategoryBundle.getPersistableBundle(type); + DataType dataType = getDataType(dataTypeBundle, dataUsage); + if (dataType != null) { + dataTypeMap.put(type, dataType); + } + } + return Collections.unmodifiableMap(dataTypeMap); + } + + /** + * Returns {@link DataType} created by parsing the {@link android.os.PersistableBundle} + * representation of DataType + */ + @Nullable + @VisibleForTesting + static DataType getDataType( + @Nullable PersistableBundle dataTypeBundle, @NonNull String dataUsage) { + if (dataTypeBundle == null || dataTypeBundle.isEmpty()) { + return null; + } + + // purposes are required, if not present, treat as invalid + int[] purposeList = dataTypeBundle.getIntArray(KEY_PURPOSES); + if (purposeList == null || purposeList.length == 0) { + return null; + } + + // Filter to set of valid purposes, and return invalid if empty + Set<Integer> purposeSet = new HashSet<>(); + for (int purpose : purposeList) { + if (DataPurposeConstants.getValidPurposes().contains(purpose)) { + purposeSet.add(purpose); + } + } + if (purposeSet.isEmpty()) { + return null; + } + + // Only set/expected for DATA COLLECTED. DATA SHARED should be null + Boolean userControl = null; + Boolean ephemeral = null; + if (DataLabelConstants.DATA_USAGE_COLLECTED.equals(dataUsage)) { + userControl = + dataTypeBundle.containsKey(KEY_USER_CONTROL) + ? dataTypeBundle.getBoolean(KEY_USER_CONTROL) + : null; + ephemeral = + dataTypeBundle.containsKey(KEY_EPHEMERAL) + ? dataTypeBundle.getBoolean(KEY_EPHEMERAL) + : null; + } + + return new DataType(purposeSet, userControl, ephemeral); + } + + /** + * Returns {@link Set} of valid {@link Integer} purposes for using the associated data category + * and type + */ + @NonNull + public Set<Integer> getPurposeSet() { + return mPurposeSet; + } + + /** + * For data-collected, returns {@code true} if data usage is user optional and {@code false} if + * data usage is required. Should return {@code null} for data-shared. + */ + @Nullable + public Boolean getUserControl() { + return mUserControl; + } + + /** + * For data-collected, returns {@code true} if data usage is user optional and {@code false} if + * data usage is processed ephemerally. Should return {@code null} for data-shared. + */ + @Nullable + public Boolean getEphemeral() { + return mEphemeral; + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java new file mode 100644 index 000000000..1b4819c41 --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java @@ -0,0 +1,421 @@ +/* + * 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.permission.safetylabel; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.StringDef; +import android.util.ArrayMap; + +import java.lang.annotation.Retention; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Constants and util methods for determining valid {@link String} data categories for usage within + * {@link SafetyLabel}, {@link DataCategory}, and {@link DataType} + */ +public class DataTypeConstants { + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_PERSONAL} + */ + @Retention(SOURCE) + @StringDef( + prefix = "PERSONAL_", + value = { + PERSONAL_NAME, + PERSONAL_EMAIL_ADDRESS, + PERSONAL_PHYSICAL_ADDRESS, + PERSONAL_PHONE_NUMBER, + PERSONAL_RACE_ETHNICITY, + PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS, + PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY, + PERSONAL_IDENTIFIERS, + PERSONAL_OTHER, + }) + public @interface PersonalType {} + + public static final String PERSONAL_NAME = "NAME"; + public static final String PERSONAL_EMAIL_ADDRESS = "EMAIL_ADDRESS"; + public static final String PERSONAL_PHYSICAL_ADDRESS = "PHYSICAL_ADDRESS"; + public static final String PERSONAL_PHONE_NUMBER = "PHONE_NUMBER"; + public static final String PERSONAL_RACE_ETHNICITY = "RACE_ETHNICITY"; + public static final String PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS = + "POLITICAL_OR_RELIGIOUS_BELIEFS"; + public static final String PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY = + "SEXUAL_ORIENTATION_OR_GENDER_IDENTITY"; + public static final String PERSONAL_IDENTIFIERS = "PERSONAL_IDENTIFIERS"; + public static final String PERSONAL_OTHER = "OTHER"; + + @PersonalType + private static final Set<String> VALID_TYPES_PERSONAL = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + PERSONAL_NAME, + PERSONAL_EMAIL_ADDRESS, + PERSONAL_PHYSICAL_ADDRESS, + PERSONAL_PHONE_NUMBER, + PERSONAL_RACE_ETHNICITY, + PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS, + PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY, + PERSONAL_IDENTIFIERS, + PERSONAL_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_FINANCIAL} + */ + @Retention(SOURCE) + @StringDef( + prefix = "FINANCIAL_", + value = { + FINANCIAL_CARD_BANK_ACCOUNT, + FINANCIAL_PURCHASE_HISTORY, + FINANCIAL_CREDIT_SCORE, + FINANCIAL_OTHER, + }) + public @interface FinancialType {} + + public static final String FINANCIAL_CARD_BANK_ACCOUNT = "CARD_BANK_ACCOUNT"; + public static final String FINANCIAL_PURCHASE_HISTORY = "PURCHASE_HISTORY"; + public static final String FINANCIAL_CREDIT_SCORE = "CREDIT_SCORE"; + public static final String FINANCIAL_OTHER = "OTHER"; + + @FinancialType + private static final Set<String> VALID_TYPES_FINANCIAL = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + FINANCIAL_CARD_BANK_ACCOUNT, + FINANCIAL_PURCHASE_HISTORY, + FINANCIAL_CREDIT_SCORE, + FINANCIAL_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_LOCATION} + */ + @Retention(SOURCE) + @StringDef( + prefix = "LOCATION_", + value = { + LOCATION_APPROX_LOCATION, + LOCATION_PRECISE_LOCATION, + }) + public @interface LocationType {} + + public static final String LOCATION_APPROX_LOCATION = "APPROX_LOCATION"; + public static final String LOCATION_PRECISE_LOCATION = "PRECISE_LOCATION"; + + @LocationType + private static final Set<String> VALID_TYPES_LOCATION = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList(LOCATION_APPROX_LOCATION, LOCATION_PRECISE_LOCATION))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_EMAIL_TEXT_MESSAGE} + */ + @Retention(SOURCE) + @StringDef( + prefix = "EMAIL_TEXT_MESSAGE_", + value = { + EMAIL_TEXT_MESSAGE_EMAILS, + EMAIL_TEXT_MESSAGE_TEXT_MESSAGES, + EMAIL_TEXT_MESSAGE_OTHER, + }) + public @interface EmailTextMessageType {} + + public static final String EMAIL_TEXT_MESSAGE_EMAILS = "EMAILS"; + public static final String EMAIL_TEXT_MESSAGE_TEXT_MESSAGES = "TEXT_MESSAGES"; + public static final String EMAIL_TEXT_MESSAGE_OTHER = "OTHER"; + + @EmailTextMessageType + private static final Set<String> VALID_TYPES_EMAIL_TEXT_MESSAGE = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + EMAIL_TEXT_MESSAGE_EMAILS, + EMAIL_TEXT_MESSAGE_TEXT_MESSAGES, + EMAIL_TEXT_MESSAGE_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_PHOTO_VIDEO} + */ + @Retention(SOURCE) + @StringDef( + prefix = "PHOTO_VIDEO_", + value = { + PHOTO_VIDEO_PHOTOS, + PHOTO_VIDEO_VIDEOS, + }) + public @interface PhotoVideoType {} + + public static final String PHOTO_VIDEO_PHOTOS = "PHOTOS"; + public static final String PHOTO_VIDEO_VIDEOS = "VIDEOS"; + + @PhotoVideoType + private static final Set<String> VALID_TYPES_PHOTO_VIDEO = + Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(PHOTO_VIDEO_PHOTOS, PHOTO_VIDEO_VIDEOS))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_AUDIO} + */ + @Retention(SOURCE) + @StringDef( + prefix = "AUDIO_", + value = {AUDIO_SOUND_RECORDINGS, AUDIO_MUSIC_FILES, AUDIO_OTHER}) + public @interface AudioType {} + + public static final String AUDIO_SOUND_RECORDINGS = "SOUND_RECORDINGS"; + public static final String AUDIO_MUSIC_FILES = "MUSIC_FILES"; + public static final String AUDIO_OTHER = "OTHER"; + + @AudioType + private static final Set<String> VALID_TYPES_AUDIO = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList(AUDIO_SOUND_RECORDINGS, AUDIO_MUSIC_FILES, AUDIO_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_STORAGE} + */ + @Retention(SOURCE) + @StringDef( + prefix = "STORAGE_", + value = { + STORAGE_FILES_DOCS, + }) + public @interface StorageType {} + + public static final String STORAGE_FILES_DOCS = "FILES_DOCS"; + + @StorageType + private static final Set<String> VALID_TYPES_STORAGE = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(STORAGE_FILES_DOCS))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_HEALTH_FITNESS} + */ + @Retention(SOURCE) + @StringDef( + prefix = "HEALTH_FITNESS_", + value = { + HEALTH_FITNESS_HEALTH, + HEALTH_FITNESS_FITNESS, + }) + public @interface HealthFitnessType {} + + public static final String HEALTH_FITNESS_HEALTH = "HEALTH"; + public static final String HEALTH_FITNESS_FITNESS = "FITNESS"; + + @HealthFitnessType + private static final Set<String> VALID_TYPES_HEALTH_FITNESS = + Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(HEALTH_FITNESS_HEALTH, HEALTH_FITNESS_FITNESS))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_CONTACTS} + */ + @Retention(SOURCE) + @StringDef( + prefix = "CONTACTS_", + value = { + CONTACTS_CONTACTS, + }) + public @interface ContactsType {} + + public static final String CONTACTS_CONTACTS = "CONTACTS"; + + @ContactsType + private static final Set<String> VALID_TYPES_CONTACTS = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CONTACTS_CONTACTS))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_CALENDAR} + */ + @Retention(SOURCE) + @StringDef( + prefix = "CALENDAR_", + value = { + CALENDAR_CALENDAR, + }) + public @interface CalendarType {} + + public static final String CALENDAR_CALENDAR = "CALENDAR"; + + @CalendarType + private static final Set<String> VALID_TYPES_CALENDAR = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CALENDAR_CALENDAR))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_IDENTIFIERS} + */ + @Retention(SOURCE) + @StringDef( + prefix = "IDENTIFIERS_", + value = { + IDENTIFIERS_OTHER, + }) + public @interface IdentifiersType {} + + public static final String IDENTIFIERS_OTHER = "OTHER"; + + @IdentifiersType + private static final Set<String> VALID_TYPES_IDENTIFIERS = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(IDENTIFIERS_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_APP_PERFORMANCE} + */ + @Retention(SOURCE) + @StringDef( + prefix = "APP_PERFORMANCE_", + value = { + APP_PERFORMANCE_CRASH_LOGS, + APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS, + APP_PERFORMANCE_OTHER, + }) + public @interface AppPerformanceType {} + + public static final String APP_PERFORMANCE_CRASH_LOGS = "CRASH_LOGS"; + public static final String APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS = "PERFORMANCE_DIAGNOSTICS"; + public static final String APP_PERFORMANCE_OTHER = "OTHER"; + + @AppPerformanceType + private static final Set<String> VALID_TYPES_APP_PERFORMANCE = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + APP_PERFORMANCE_CRASH_LOGS, + APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS, + APP_PERFORMANCE_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_ACTIONS_IN_APP} + */ + @Retention(SOURCE) + @StringDef( + prefix = "ACTIONS_IN_APP_", + value = { + ACTIONS_IN_APP_USER_INTERACTION, + ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY, + ACTIONS_IN_APP_INSTALLED_APPS, + ACTIONS_IN_APP_USER_GENERATED_CONTENT, + ACTIONS_IN_APP_OTHER, + }) + public @interface ActionsInAppType {} + + public static final String ACTIONS_IN_APP_USER_INTERACTION = "USER_INTERACTION"; + public static final String ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY = "IN_APP_SEARCH_HISTORY"; + public static final String ACTIONS_IN_APP_INSTALLED_APPS = "INSTALLED_APPS"; + public static final String ACTIONS_IN_APP_USER_GENERATED_CONTENT = "USER_GENERATED_CONTENT"; + public static final String ACTIONS_IN_APP_OTHER = "OTHER"; + + @ActionsInAppType + private static final Set<String> VALID_TYPES_ACTIONS_IN_APP = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + ACTIONS_IN_APP_USER_INTERACTION, + ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY, + ACTIONS_IN_APP_INSTALLED_APPS, + ACTIONS_IN_APP_USER_GENERATED_CONTENT, + ACTIONS_IN_APP_OTHER))); + + /** + * List of valid Safety Label data collection/sharing types for {@link + * DataCategoryConstants#CATEGORY_SEARCH_AND_BROWSING} + */ + @Retention(SOURCE) + @StringDef( + prefix = "SEARCH_AND_BROWSING_", + value = { + SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY, + }) + public @interface SearchAndBrowsingType {} + + public static final String SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY = "WEB_BROWSING_HISTORY"; + + @SearchAndBrowsingType + private static final Set<String> VALID_TYPES_SEARCH_AND_BROWSING = + Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY))); + + private static final Map<String, Set<String>> VALID_TYPES_FOR_CATEGORY_MAP; + + /** Returns {@link Set} of valid types for the specified {@link String} category key */ + public static Set<String> getValidDataTypesForCategory( + @DataCategoryConstants.Category String category) { + return VALID_TYPES_FOR_CATEGORY_MAP.containsKey(category) + ? VALID_TYPES_FOR_CATEGORY_MAP.get(category) + : Collections.emptySet(); + } + + static { + VALID_TYPES_FOR_CATEGORY_MAP = new ArrayMap<>(); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_PERSONAL, VALID_TYPES_PERSONAL); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_FINANCIAL, VALID_TYPES_FINANCIAL); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_LOCATION, VALID_TYPES_LOCATION); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_EMAIL_TEXT_MESSAGE, VALID_TYPES_EMAIL_TEXT_MESSAGE); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_PHOTO_VIDEO, VALID_TYPES_PHOTO_VIDEO); + VALID_TYPES_FOR_CATEGORY_MAP.put(DataCategoryConstants.CATEGORY_AUDIO, VALID_TYPES_AUDIO); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_STORAGE, VALID_TYPES_STORAGE); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_HEALTH_FITNESS, VALID_TYPES_HEALTH_FITNESS); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_CONTACTS, VALID_TYPES_CONTACTS); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_CALENDAR, VALID_TYPES_CALENDAR); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_IDENTIFIERS, VALID_TYPES_IDENTIFIERS); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_APP_PERFORMANCE, VALID_TYPES_APP_PERFORMANCE); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_ACTIONS_IN_APP, VALID_TYPES_ACTIONS_IN_APP); + VALID_TYPES_FOR_CATEGORY_MAP.put( + DataCategoryConstants.CATEGORY_SEARCH_AND_BROWSING, + VALID_TYPES_SEARCH_AND_BROWSING); + } + + private DataTypeConstants() { + /* do nothing - hide constructor */ + } +} diff --git a/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java new file mode 100644 index 000000000..214b2db54 --- /dev/null +++ b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java @@ -0,0 +1,59 @@ +/* + * 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.permission.safetylabel; + +import android.os.PersistableBundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +/** Safety Label representation containing zero or more {@link DataCategory} for data shared */ +public class SafetyLabel { + @VisibleForTesting static final String KEY_SAFETY_LABEL = "safety_labels"; + private final DataLabel mDataLabel; + + private SafetyLabel(@NonNull DataLabel dataLabel) { + this.mDataLabel = dataLabel; + } + + /** Returns {@link SafetyLabel} created by parsing a metadata {@link PersistableBundle} */ + @Nullable + public static SafetyLabel getSafetyLabelFromMetadata(@Nullable PersistableBundle bundle) { + if (bundle == null) { + return null; + } + + PersistableBundle safetyLabelBundle = bundle.getPersistableBundle(KEY_SAFETY_LABEL); + if (safetyLabelBundle == null) { + return null; + } + + DataLabel dataLabel = DataLabel.getDataLabel(safetyLabelBundle); + if (dataLabel == null) { + return null; + } + + return new SafetyLabel(dataLabel); + } + + /** Returns the data label for the safety label */ + @NonNull + public DataLabel getDataLabel() { + return mDataLabel; + } +} diff --git a/SafetyLabel/tests/Android.bp b/SafetyLabel/tests/Android.bp new file mode 100644 index 000000000..2026a6ac8 --- /dev/null +++ b/SafetyLabel/tests/Android.bp @@ -0,0 +1,38 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test { + name: "SafetyLabelTests", + defaults: ["mts-target-sdk-version-current"], + sdk_version: "test_current", + min_sdk_version: "30", + target_sdk_version: "32", + srcs: [ + "java/**/*.kt", + ], + per_testcase_directory: true, + static_libs: [ + "compatibility-device-util-axt", + "kotlinx-coroutines-android", + "safety-label", + ], + test_suites: [ + "general-tests", + "mts-permission", + ], +} diff --git a/SafetyLabel/tests/AndroidManifest.xml b/SafetyLabel/tests/AndroidManifest.xml new file mode 100644 index 000000000..4d1e62905 --- /dev/null +++ b/SafetyLabel/tests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ 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. + --> + +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.permission.safetylabel.tests"> + + <application android:label="Safety Label Tests"> + <uses-library android:name="android.test.runner"/> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.permission.safetylabel.tests" + android:label="Tests for the Safety Label library"/> +</manifest> diff --git a/SafetyLabel/tests/AndroidTest.xml b/SafetyLabel/tests/AndroidTest.xml new file mode 100644 index 000000000..919e7ce2a --- /dev/null +++ b/SafetyLabel/tests/AndroidTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ 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. + --> + +<configuration description="Runs unit tests for the Safety Label library."> + <option name="test-tag" value="SafetyLabelTests"/> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController"/> + + <!-- Install test --> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="SafetyLabelTests.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <!-- Create place to store apks --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="mkdir -p /data/local/tmp/safetylabel/tests/"/> + <option name="teardown-command" value="rm -rf /data/local/tmp/safetylabel/"/> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.permission.safetylabel.tests"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt new file mode 100644 index 000000000..8ed705064 --- /dev/null +++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt @@ -0,0 +1,261 @@ +/* + * 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.permission.safetylabel + +import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION +import com.android.permission.safetylabel.DataCategoryConstants.VALID_CATEGORIES +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.INVALID_KEY +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createCategoryMapPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createCategoryMapPersistableBundleWithInvalidTypes +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createDataLabelPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createDataLabelPersistableBundleWithAdditonalInvalidCategory +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidCategoryMapPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidDataLabelPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypePersistableBundle +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** CTS tests for [DataCategory]. */ +@RunWith(AndroidJUnit4::class) +class DataCategoryTest { + @Test + fun getDataCategoryMap_dataUsageCollected_nullPersistableBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap(null, DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageCollected_emptyPersistableBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageCollected_invalidBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createInvalidDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageCollected_validBundle_hasAllValidCategories() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES) + } + + @Test + fun getDataCategoryMap_dataUsageCollected_additionalInvalidCategory_hasOnlyValidCategories() { + // Create valid data label and put an additional invalid/unknown category key + val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory() + + val dataCategoryMap = + DataCategory.getDataCategoryMap( + dataLabelBundle, DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES) + } + + @Test + fun getDataCategoryMap_dataUsageShared_nullPersistableBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap(null, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageShared_emptyPersistableBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageShared_invalidBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createInvalidDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageShared_validBundle_hasAllValidCategories() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES) + } + + @Test + fun getDataCategoryMap_dataUsageShared_additionalInvalidCategory_hasOnlyValidCategories() { + // Create valid data label and put an additional invalid/unknown category key + val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory() + + val dataCategoryMap = + DataCategory.getDataCategoryMap(dataLabelBundle, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES) + } + + @Test + fun getDataCategoryMap_dataUsageInvalid_nullPersistableBundle_emptyMap() { + val dataCategoryMap = DataCategory.getDataCategoryMap(null, "invalid_datausage_key") + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageInvalid_emptyPersistableBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap(PersistableBundle.EMPTY, "invalid_datausage_key") + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageInvalid_invalidBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createInvalidDataLabelPersistableBundle(), "invalid_datausage_key") + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageInvalid_validBundle_emptyMap() { + val dataCategoryMap = + DataCategory.getDataCategoryMap( + createDataLabelPersistableBundle(), "invalid_datausage_key") + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategoryMap_dataUsageInvalid_validBundleWithAdditionalInvalidCategory_null() { + // Create valid data label and put an additional invalid/unknown category key + val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory() + + val dataCategoryMap = + DataCategory.getDataCategoryMap(dataLabelBundle, "invalid_datausage_key") + + assertThat(dataCategoryMap).isNotNull() + assertThat(dataCategoryMap).isEmpty() + } + + @Test + fun getDataCategory_nullBundle_nullDataCategory() { + val dataCategory = + DataCategory.getDataCategory( + null, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataCategory).isNull() + } + + @Test + fun getDataCategory_emptyBundle_nullDataCategory() { + val dataCategory = + DataCategory.getDataCategory( + PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataCategory).isNull() + } + + @Test + fun getDataCategory_invalidBundle_nullDataCategory() { + val dataCategory = + DataCategory.getDataCategory( + createInvalidCategoryMapPersistableBundle(), + DataLabelConstants.DATA_USAGE_SHARED, + CATEGORY_LOCATION) + + assertThat(dataCategory).isNull() + } + + @Test + fun getDataCategory_validCategoriesAndInvalidType_nullDataCategory() { + val dataCategory = + DataCategory.getDataCategory( + createCategoryMapPersistableBundleWithInvalidTypes(), + DataLabelConstants.DATA_USAGE_SHARED, + INVALID_KEY) + + assertThat(dataCategory).isNull() + } + + @Test + fun getDataCategory_validBundle_validCategoryAndExpectedTypes() { + val dataCategory = + DataCategory.getDataCategory( + createCategoryMapPersistableBundle(), + DataLabelConstants.DATA_USAGE_SHARED, + CATEGORY_LOCATION) + + assertThat(dataCategory).isNotNull() + assertThat(dataCategory?.dataTypes).isNotEmpty() + assertThat(dataCategory?.dataTypes?.keys) + .containsExactlyElementsIn( + DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION)) + } + + @Test + fun getDataCategory_validBundleWithAddedInvalidType_validCategoryAndOnlyExpectedTypes() { + // Create valid bundle with additional invalid type + val dataTypeMapBundle = createCategoryMapPersistableBundle() + dataTypeMapBundle.putPersistableBundle(INVALID_KEY, createTypePersistableBundle()) + + val dataCategory = + DataCategory.getDataCategory( + dataTypeMapBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataCategory).isNotNull() + assertThat(dataCategory?.dataTypes).isNotEmpty() + assertThat(dataCategory?.dataTypes?.keys) + .containsExactlyElementsIn( + DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION)) + } +} diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt new file mode 100644 index 000000000..1c0c56238 --- /dev/null +++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt @@ -0,0 +1,126 @@ +/* + * 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.permission.safetylabel + +import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidSafetyLabelPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithEmptyDataCollected +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithEmptyDataShared +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithInvalidDataCollected +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithInvalidDataShared +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithNullDataCollected +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithNullDataShared +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** CTS tests for [DataLabel]. */ +@RunWith(AndroidJUnit4::class) +class DataLabelTest { + @Test + fun getDataLabel_nullBundle_nullDataLabel() { + val dataLabel: DataLabel? = DataLabel.getDataLabel(null) + + assertThat(dataLabel).isNull() + } + + @Test + fun getDataLabel_emptyBundle_nullDataLabel() { + val dataLabel: DataLabel? = DataLabel.getDataLabel(PersistableBundle.EMPTY) + + assertThat(dataLabel).isNull() + } + + @Test + fun getDataLabel_invalidBundle_nullDataLabel() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createInvalidSafetyLabelPersistableBundle()) + + assertThat(dataLabel).isNull() + } + + @Test + fun getDataLabel_nullDataCollectedBundle_dataCollectedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithNullDataCollected()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isEmpty() + assertThat(dataLabel?.dataShared).isNotEmpty() + } + + @Test + fun getDataLabel_nullDataSharedBundle_dataSharedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithNullDataShared()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isNotEmpty() + assertThat(dataLabel?.dataShared).isEmpty() + } + + @Test + fun getDataLabel_emptyDataCollectedBundle_dataCollectedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithEmptyDataCollected()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isEmpty() + assertThat(dataLabel?.dataShared).isNotEmpty() + } + + @Test + fun getDataLabel_emptyDataSharedBundle_dataSharedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithEmptyDataShared()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isNotEmpty() + assertThat(dataLabel?.dataShared).isEmpty() + } + + @Test + fun getDataLabel_invalidDataCollectedBundle_dataCollectedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithInvalidDataCollected()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isEmpty() + assertThat(dataLabel?.dataShared).isNotEmpty() + } + + @Test + fun getDataLabel_invalidDataSharedBundle_dataSharedEmpty() { + val dataLabel: DataLabel? = + DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithInvalidDataShared()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isNotEmpty() + assertThat(dataLabel?.dataShared).isEmpty() + } + + @Test + fun getDataLabel_validBundle() { + val dataLabel: DataLabel? = DataLabel.getDataLabel(createSafetyLabelPersistableBundle()) + + assertThat(dataLabel).isNotNull() + assertThat(dataLabel?.dataCollected).isNotEmpty() + assertThat(dataLabel?.dataShared).isNotEmpty() + } +} diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt new file mode 100644 index 000000000..bfc9be726 --- /dev/null +++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt @@ -0,0 +1,280 @@ +/* + * 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.permission.safetylabel + +import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION +import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING +import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_APP_FUNCTIONALITY +import com.android.permission.safetylabel.DataType.KEY_EPHEMERAL +import com.android.permission.safetylabel.DataType.KEY_PURPOSES +import com.android.permission.safetylabel.DataType.KEY_USER_CONTROL +import com.android.permission.safetylabel.DataTypeConstants.LOCATION_APPROX_LOCATION +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.INVALID_KEY +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidTypeMapPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidTypePersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypeMapPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypeMapWithInvalidTypeDataPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypePersistableBundle +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** CTS tests for [DataType]. */ +@RunWith(AndroidJUnit4::class) +class DataTypeTest { + @Test + fun getDataTypeMap_invalidCategory_nullPersistableBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap(null, DataLabelConstants.DATA_USAGE_SHARED, INVALID_KEY) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_invalidCategory_emptyPersistableBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap( + PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, INVALID_KEY) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_invalidCategory_invalidBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap( + createInvalidTypeMapPersistableBundle(), + DataLabelConstants.DATA_USAGE_SHARED, + INVALID_KEY) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_invalidCategory_validBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap( + createTypeMapPersistableBundle(CATEGORY_LOCATION), + DataLabelConstants.DATA_USAGE_SHARED, + INVALID_KEY) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_validCategory_nullPersistableBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap(null, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_validCategory_emptyPersistableBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap( + PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_validCategory_invalidBundle_emptyMap() { + val dataTypeMap = + DataType.getDataTypeMap( + createInvalidTypeMapPersistableBundle(), + DataLabelConstants.DATA_USAGE_SHARED, + CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_validCategory_validBundle_hasAllExpectedTypes() { + val typeMapPersistableBundle = createTypeMapPersistableBundle(CATEGORY_LOCATION) + + val dataTypeMap = + DataType.getDataTypeMap( + typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap.keys) + .containsExactlyElementsIn( + DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION)) + } + + @Test + fun getDataTypeMap_validCategory_validBundleWithAddedInvalidType_hasOnlyExpectedTypes() { + val typeMapPersistableBundle = createTypeMapPersistableBundle(CATEGORY_LOCATION) + // Add additional valid persistable bundle under invalid key + typeMapPersistableBundle.putPersistableBundle(INVALID_KEY, createTypePersistableBundle()) + + val dataTypeMap = + DataType.getDataTypeMap( + typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap.keys) + .containsExactlyElementsIn( + DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION)) + assertThat(dataTypeMap.keys).doesNotContain(INVALID_KEY) + } + + @Test + fun getDataTypeMap_validCategory_validType_invalidData_emptyMap() { + val typeMapPersistableBundle = + createTypeMapWithInvalidTypeDataPersistableBundle(CATEGORY_LOCATION) + + val dataTypeMap = + DataType.getDataTypeMap( + typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).isEmpty() + } + + @Test + fun getDataTypeMap_dataCollected_validCategory_validBundle_validateSingleExpectedType() { + val dataTypeMap = + DataType.getDataTypeMap( + createTypeMapPersistableBundle(CATEGORY_LOCATION), + DataLabelConstants.DATA_USAGE_COLLECTED, + CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).containsKey(LOCATION_APPROX_LOCATION) + val type = dataTypeMap[LOCATION_APPROX_LOCATION]!! + assertThat(type.purposeSet).containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + assertThat(type.userControl).isTrue() + assertThat(type.ephemeral).isTrue() + } + + @Test + fun getDataTypeMap_dataShared_validCategory_validBundle_validateSingleExpectedType() { + val dataTypeMap = + DataType.getDataTypeMap( + createTypeMapPersistableBundle(CATEGORY_LOCATION), + DataLabelConstants.DATA_USAGE_SHARED, + CATEGORY_LOCATION) + + assertThat(dataTypeMap).isNotNull() + assertThat(dataTypeMap).containsKey(LOCATION_APPROX_LOCATION) + val type = dataTypeMap[LOCATION_APPROX_LOCATION]!! + assertThat(type.purposeSet).containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + assertThat(type.userControl).isNull() + assertThat(type.ephemeral).isNull() + } + + @Test + fun getDataType_invalidBundle_nullDataType() { + val dataType = + DataType.getDataType( + createInvalidTypePersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataType).isNull() + } + + @Test + fun getDataType_dataCollected_validDataType() { + val dataType = + DataType.getDataType( + createTypePersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataType).isNotNull() + assertThat(dataType?.purposeSet) + .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + assertThat(dataType?.userControl).isTrue() + assertThat(dataType?.ephemeral).isTrue() + } + + @Test + fun getDataType_dataShared_validDataType() { + val dataType = + DataType.getDataType( + createTypePersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataType).isNotNull() + assertThat(dataType?.purposeSet) + .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + assertThat(dataType?.userControl).isNull() + assertThat(dataType?.ephemeral).isNull() + } + + @Test + fun getDataType_validDataTypeWithAddedInvalidPurpose_onlyValidPurposes() { + val typePersistableBundle = createTypePersistableBundle() + val purposes: IntArray = typePersistableBundle.getIntArray(KEY_PURPOSES)!! + val updatedPurposes: IntArray = intArrayOf(-1, *purposes) + typePersistableBundle.putIntArray(KEY_PURPOSES, updatedPurposes) + + val dataType = + DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataType).isNotNull() + // Should not contain the additional "-1" purpose added above + assertThat(dataType?.purposeSet) + .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + } + + @Test + fun getDataType_dataTypeWithInvalidPurpose_nullDataType() { + val typePersistableBundle = createTypePersistableBundle() + typePersistableBundle.remove(KEY_PURPOSES) + val updatedPurposes: IntArray = intArrayOf(-1) + typePersistableBundle.putIntArray(KEY_PURPOSES, updatedPurposes) + + val dataType = + DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataType).isNull() + } + + @Test + fun getDataType_noPurpose_nullDataType() { + val typePersistableBundle = createTypePersistableBundle() + typePersistableBundle.remove(KEY_PURPOSES) + + val dataType = + DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED) + + assertThat(dataType).isNull() + } + + @Test + fun getDataType_dataCollected_validDataType_noUserControl_noEphemeral() { + val bundle = createTypePersistableBundle() + bundle.remove(KEY_USER_CONTROL) + bundle.remove(KEY_EPHEMERAL) + + val dataType = DataType.getDataType(bundle, DataLabelConstants.DATA_USAGE_COLLECTED) + + assertThat(dataType).isNotNull() + assertThat(dataType?.purposeSet) + .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING) + assertThat(dataType?.userControl).isNull() + assertThat(dataType?.ephemeral).isNull() + } +} diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt new file mode 100644 index 000000000..ce9f24634 --- /dev/null +++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt @@ -0,0 +1,70 @@ +/* + * 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.permission.safetylabel + +import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidMetadataPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createMetadataPersistableBundle +import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createMetadataPersistableBundleWithInvalidSafetyLabel +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** CTS tests for [SafetyLabel]. */ +@RunWith(AndroidJUnit4::class) +class SafetyLabelTest { + + @Test + fun getSafetyLabelFromMetaData_nullMetadataBundle_nullSafetyLabel() { + val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(null) + + assertThat(safetyLabel).isNull() + } + + @Test + fun getSafetyLabelFromMetaData_emptyMetadataBundle_nullSafetyLabel() { + val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(PersistableBundle.EMPTY) + + assertThat(safetyLabel).isNull() + } + + @Test + fun getSafetyLabelFromMetaData_invalidMetadataBundle_nullSafetyLabel() { + val safetyLabel = + SafetyLabel.getSafetyLabelFromMetadata(createInvalidMetadataPersistableBundle()) + + assertThat(safetyLabel).isNull() + } + + @Test + fun getSafetyLabelFromMetaData_invalidSafetyLabelBundle_dataSharedEmpty() { + val safetyLabel = + SafetyLabel.getSafetyLabelFromMetadata( + createMetadataPersistableBundleWithInvalidSafetyLabel()) + + assertThat(safetyLabel).isNull() + } + + @Test + fun getSafetyLabelFromMetaData_validBundle_hasDataShared() { + val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(createMetadataPersistableBundle()) + + assertThat(safetyLabel).isNotNull() + assertThat(safetyLabel?.dataLabel).isNotNull() + } +} diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt new file mode 100644 index 000000000..2d2811633 --- /dev/null +++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt @@ -0,0 +1,261 @@ +/* + * 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.permission.safetylabel + +import android.os.PersistableBundle +import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION +import com.android.permission.safetylabel.DataCategoryConstants.Category +import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING +import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_APP_FUNCTIONALITY +import com.android.permission.safetylabel.DataType.KEY_EPHEMERAL +import com.android.permission.safetylabel.DataType.KEY_PURPOSES +import com.android.permission.safetylabel.DataType.KEY_USER_CONTROL + +/** A class that facilitates creating test safety label persistable bundles. */ +object SafetyLabelTestPersistableBundles { + const val INVALID_KEY = "invalid_key" + + fun createMetadataPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(SafetyLabel.KEY_SAFETY_LABEL, createSafetyLabelPersistableBundle()) + } + } + + fun createInvalidMetadataPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(INVALID_KEY, createSafetyLabelPersistableBundle()) + } + } + + fun createMetadataPersistableBundleWithInvalidSafetyLabel(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + SafetyLabel.KEY_SAFETY_LABEL, createInvalidSafetyLabelPersistableBundle()) + } + } + + /** Returns [PersistableBundle] representation of a valid safety label */ + fun createSafetyLabelPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundle()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createInvalidSafetyLabelPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(INVALID_KEY, createDataLabelPersistableBundle()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithNullDataCollected(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithNullDataCollected()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithNullDataShared(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithNullDataShared()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithEmptyDataCollected(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithEmptyDataCollected()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithEmptyDataShared(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithEmptyDataShared()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithInvalidDataCollected(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, + createDataLabelPersistableBundleWithInvalidDataCollected()) + } + } + + /** Returns [PersistableBundle] representation of an ivnalid safety label */ + fun createSafetyLabelPersistableBundleWithInvalidDataShared(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithInvalidDataShared()) + } + } + + fun createDataLabelPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabelConstants.DATA_USAGE_SHARED, createCategoryMapPersistableBundle()) + putPersistableBundle( + DataLabelConstants.DATA_USAGE_COLLECTED, createCategoryMapPersistableBundle()) + } + } + + fun createInvalidDataLabelPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(INVALID_KEY, createCategoryMapPersistableBundle()) + } + } + + private fun createDataLabelPersistableBundleWithNullDataCollected(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED) + return bundle + } + + private fun createDataLabelPersistableBundleWithNullDataShared(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_SHARED) + return bundle + } + + private fun createDataLabelPersistableBundleWithEmptyDataCollected(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED) + bundle.putPersistableBundle( + DataLabelConstants.DATA_USAGE_COLLECTED, PersistableBundle.EMPTY) + return bundle + } + + private fun createDataLabelPersistableBundleWithEmptyDataShared(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_SHARED) + bundle.putPersistableBundle(DataLabelConstants.DATA_USAGE_SHARED, PersistableBundle.EMPTY) + return bundle + } + + private fun createDataLabelPersistableBundleWithInvalidDataCollected(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED) + bundle.putPersistableBundle( + DataLabelConstants.DATA_USAGE_COLLECTED, createInvalidCategoryMapPersistableBundle()) + return bundle + } + + private fun createDataLabelPersistableBundleWithInvalidDataShared(): PersistableBundle { + val bundle = createDataLabelPersistableBundle() + bundle.remove(DataLabelConstants.DATA_USAGE_SHARED) + bundle.putPersistableBundle( + DataLabelConstants.DATA_USAGE_SHARED, createInvalidCategoryMapPersistableBundle()) + return bundle + } + + fun createDataLabelPersistableBundleWithAdditonalInvalidCategory(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle( + DataLabelConstants.DATA_USAGE_SHARED, + createCategoryMapPersistableBundleWithAdditionalInvalidCategory()) + putPersistableBundle( + DataLabelConstants.DATA_USAGE_COLLECTED, + createCategoryMapPersistableBundleWithAdditionalInvalidCategory()) + } + } + + /** Returns [PersistableBundle] representation of a [Map] of valid data categories */ + fun createCategoryMapPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey -> + putPersistableBundle(categoryKey, createTypeMapPersistableBundle(categoryKey)) + } + } + } + + /** Returns [PersistableBundle] representation of a [Map] of valid data categories */ + fun createCategoryMapPersistableBundleWithAdditionalInvalidCategory(): PersistableBundle { + return PersistableBundle().apply { + DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey -> + putPersistableBundle(categoryKey, createTypeMapPersistableBundle(categoryKey)) + } + putPersistableBundle(INVALID_KEY, createTypeMapPersistableBundle(CATEGORY_LOCATION)) + } + } + + /** + * Returns [PersistableBundle] representation of a [Map] of valid data categories and invalid + * types + */ + fun createCategoryMapPersistableBundleWithInvalidTypes(): PersistableBundle { + return PersistableBundle().apply { + DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey -> + putPersistableBundle(categoryKey, createInvalidTypeMapPersistableBundle()) + } + } + } + + /** Returns [PersistableBundle] representation of a [Map] of invalid data categories */ + fun createInvalidCategoryMapPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(INVALID_KEY, createTypeMapPersistableBundle(CATEGORY_LOCATION)) + } + } + + /** Returns [PersistableBundle] representation of a [Map] of valid data type */ + fun createTypeMapPersistableBundle(@Category category: String): PersistableBundle { + return PersistableBundle().apply { + DataTypeConstants.getValidDataTypesForCategory(category).forEach { type -> + putPersistableBundle(type, createTypePersistableBundle()) + } + } + } + + /** Returns [PersistableBundle] representation of a [Map] of invalid data type */ + fun createInvalidTypeMapPersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putPersistableBundle(INVALID_KEY, createTypePersistableBundle()) + } + } + + /** Returns [PersistableBundle] representation of a [Map] of valid type, with invalid data */ + fun createTypeMapWithInvalidTypeDataPersistableBundle( + @Category category: String + ): PersistableBundle { + return PersistableBundle().apply { + DataTypeConstants.getValidDataTypesForCategory(category).forEach { type -> + putPersistableBundle(type, createInvalidTypePersistableBundle()) + } + } + } + + /** Returns [PersistableBundle] representation of a valid data type */ + fun createTypePersistableBundle(): PersistableBundle { + return PersistableBundle().apply { + putIntArray(KEY_PURPOSES, intArrayOf(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)) + putBoolean(KEY_USER_CONTROL, true) + putBoolean(KEY_EPHEMERAL, true) + } + } + + /** Returns [PersistableBundle] representation of an invalid data type */ + fun createInvalidTypePersistableBundle(): PersistableBundle { + return PersistableBundle().apply { putLong(INVALID_KEY, 0) } + } +} diff --git a/framework-s/Android.bp b/framework-s/Android.bp index ec33f13ee..8d107d18a 100644 --- a/framework-s/Android.bp +++ b/framework-s/Android.bp @@ -34,8 +34,8 @@ filegroup { } filegroup { - name: "safetycenter-config-schema", - srcs: ["java/android/safetycenter/config/safety_center_config.xsd"], + name: "safetycenter-config-schemas", + srcs: ["java/android/safetycenter/config/safety_center_config*.xsd"], path: "java/android/safetycenter/config/", visibility: ["//packages/modules/Permission/SafetyCenter/ConfigLintChecker"], } @@ -47,6 +47,9 @@ java_library { "framework-annotations-lib", "unsupportedappusage", ], + static_libs: [ + "modules-utils-build", + ], apex_available: [ "com.android.permission", "test_com.android.permission", @@ -84,7 +87,7 @@ java_sdk_library { lint: { strict_updatability_linting: true, }, - min_sdk_version: "30", + min_sdk_version: "31", permitted_packages: [ "android.permission", "android.app.role", diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index db2d140ca..3f7cd09ec 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -36,6 +36,7 @@ package android.app.role { method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT"; + field public static final String ROLE_FINANCED_DEVICE_KIOSK = "android.app.role.FINANCED_DEVICE_KIOSK"; field public static final String ROLE_SYSTEM_ACTIVITY_RECOGNIZER = "android.app.role.SYSTEM_ACTIVITY_RECOGNIZER"; field public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION"; field public static final String ROLE_SYSTEM_WELLBEING = "android.app.role.SYSTEM_WELLBEING"; @@ -47,7 +48,9 @@ package android.safetycenter { public final class SafetyCenterData implements android.os.Parcelable { ctor public SafetyCenterData(@NonNull android.safetycenter.SafetyCenterStatus, @NonNull java.util.List<android.safetycenter.SafetyCenterIssue>, @NonNull java.util.List<android.safetycenter.SafetyCenterEntryOrGroup>, @NonNull java.util.List<android.safetycenter.SafetyCenterStaticEntryGroup>); + ctor public SafetyCenterData(@NonNull android.safetycenter.SafetyCenterStatus, @NonNull java.util.List<android.safetycenter.SafetyCenterIssue>, @NonNull java.util.List<android.safetycenter.SafetyCenterEntryOrGroup>, @NonNull java.util.List<android.safetycenter.SafetyCenterStaticEntryGroup>, @NonNull java.util.List<android.safetycenter.SafetyCenterIssue>); method public int describeContents(); + method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue> getDismissedIssues(); method @NonNull public java.util.List<android.safetycenter.SafetyCenterEntryOrGroup> getEntriesOrGroups(); method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue> getIssues(); method @NonNull public java.util.List<android.safetycenter.SafetyCenterStaticEntryGroup> getStaticEntryGroups(); @@ -149,6 +152,8 @@ package android.safetycenter { public final class SafetyCenterIssue implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue.Action> getActions(); + method @Nullable public CharSequence getAttributionTitle(); + method @Nullable public String getGroupId(); method @NonNull public String getId(); method public int getSeverityLevel(); method @Nullable public CharSequence getSubtitle(); @@ -191,7 +196,9 @@ package android.safetycenter { ctor public SafetyCenterIssue.Builder(@NonNull android.safetycenter.SafetyCenterIssue); method @NonNull public android.safetycenter.SafetyCenterIssue build(); method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setActions(@NonNull java.util.List<android.safetycenter.SafetyCenterIssue.Action>); + method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setAttributionTitle(@Nullable CharSequence); method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setDismissible(boolean); + method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setGroupId(@Nullable String); method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setId(@NonNull String); method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setSeverityLevel(int); method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setShouldConfirmDismissal(boolean); @@ -211,6 +218,7 @@ package android.safetycenter { method @Nullable @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public android.safetycenter.SafetySourceData getSafetySourceData(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.READ_SAFETY_CENTER_STATUS, android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE}) public boolean isSafetyCenterEnabled(); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void refreshSafetySources(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void refreshSafetySources(int, @NonNull java.util.List<java.lang.String>); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void removeOnSafetyCenterDataChangedListener(@NonNull android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener); method @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public void reportSafetySourceError(@NonNull String, @NonNull android.safetycenter.SafetySourceErrorDetails); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void setSafetyCenterConfigForTests(@NonNull android.safetycenter.config.SafetyCenterConfig); @@ -222,6 +230,7 @@ package android.safetycenter { field public static final String EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID"; field public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE"; field public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS = "android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS"; + field public static final String EXTRA_SAFETY_SOURCES_GROUP_ID = "android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID"; field public static final String EXTRA_SAFETY_SOURCE_ID = "android.safetycenter.extra.SAFETY_SOURCE_ID"; field public static final String EXTRA_SAFETY_SOURCE_ISSUE_ID = "android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID"; field public static final String EXTRA_SAFETY_SOURCE_USER_HANDLE = "android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE"; @@ -229,6 +238,7 @@ package android.safetycenter { field public static final int REFRESH_REASON_DEVICE_REBOOT = 300; // 0x12c field public static final int REFRESH_REASON_OTHER = 600; // 0x258 field public static final int REFRESH_REASON_PAGE_OPEN = 100; // 0x64 + field public static final int REFRESH_REASON_PERIODIC = 700; // 0x2bc field public static final int REFRESH_REASON_RESCAN_BUTTON_CLICK = 200; // 0xc8 field public static final int REFRESH_REASON_SAFETY_CENTER_ENABLED = 500; // 0x1f4 } @@ -347,9 +357,14 @@ package android.safetycenter { public final class SafetySourceIssue implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue.Action> getActions(); + method @Nullable public CharSequence getAttributionTitle(); + method @Nullable public android.safetycenter.SafetySourceIssue.Notification getCustomNotification(); + method @Nullable public String getDeduplicationId(); method @NonNull public String getId(); + method public int getIssueActionability(); method public int getIssueCategory(); method @NonNull public String getIssueTypeId(); + method public int getNotificationBehavior(); method @Nullable public android.app.PendingIntent getOnDismissPendingIntent(); method public int getSeverityLevel(); method @Nullable public CharSequence getSubtitle(); @@ -357,9 +372,19 @@ package android.safetycenter { method @NonNull public CharSequence getTitle(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetySourceIssue> CREATOR; + field public static final int ISSUE_ACTIONABILITY_AUTOMATIC = 200; // 0xc8 + field public static final int ISSUE_ACTIONABILITY_MANUAL = 0; // 0x0 + field public static final int ISSUE_ACTIONABILITY_TIP = 100; // 0x64 field public static final int ISSUE_CATEGORY_ACCOUNT = 200; // 0xc8 + field public static final int ISSUE_CATEGORY_DATA = 400; // 0x190 field public static final int ISSUE_CATEGORY_DEVICE = 100; // 0x64 field public static final int ISSUE_CATEGORY_GENERAL = 300; // 0x12c + field public static final int ISSUE_CATEGORY_PASSWORDS = 500; // 0x1f4 + field public static final int ISSUE_CATEGORY_PERSONAL_SAFETY = 600; // 0x258 + field public static final int NOTIFICATION_BEHAVIOR_DELAYED = 200; // 0xc8 + field public static final int NOTIFICATION_BEHAVIOR_IMMEDIATELY = 300; // 0x12c + field public static final int NOTIFICATION_BEHAVIOR_NEVER = 100; // 0x64 + field public static final int NOTIFICATION_BEHAVIOR_UNSPECIFIED = 0; // 0x0 } public static final class SafetySourceIssue.Action implements android.os.Parcelable { @@ -385,11 +410,33 @@ package android.safetycenter { method @NonNull public android.safetycenter.SafetySourceIssue.Builder addAction(@NonNull android.safetycenter.SafetySourceIssue.Action); method @NonNull public android.safetycenter.SafetySourceIssue build(); method @NonNull public android.safetycenter.SafetySourceIssue.Builder clearActions(); + method @NonNull public android.safetycenter.SafetySourceIssue.Builder setAttributionTitle(@Nullable CharSequence); + method @NonNull public android.safetycenter.SafetySourceIssue.Builder setCustomNotification(@Nullable android.safetycenter.SafetySourceIssue.Notification); + method @NonNull public android.safetycenter.SafetySourceIssue.Builder setDeduplicationId(@Nullable String); + method @NonNull public android.safetycenter.SafetySourceIssue.Builder setIssueActionability(int); method @NonNull public android.safetycenter.SafetySourceIssue.Builder setIssueCategory(int); + method @NonNull public android.safetycenter.SafetySourceIssue.Builder setNotificationBehavior(int); method @NonNull public android.safetycenter.SafetySourceIssue.Builder setOnDismissPendingIntent(@Nullable android.app.PendingIntent); method @NonNull public android.safetycenter.SafetySourceIssue.Builder setSubtitle(@Nullable CharSequence); } + public static final class SafetySourceIssue.Notification implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue.Action> getActions(); + method @NonNull public CharSequence getText(); + method @NonNull public CharSequence getTitle(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetySourceIssue.Notification> CREATOR; + } + + public static final class SafetySourceIssue.Notification.Builder { + ctor public SafetySourceIssue.Notification.Builder(@NonNull CharSequence, @NonNull CharSequence); + method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder addAction(@NonNull android.safetycenter.SafetySourceIssue.Action); + method @NonNull public android.safetycenter.SafetySourceIssue.Notification build(); + method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder clearActions(); + method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder setActions(@NonNull java.util.List<android.safetycenter.SafetySourceIssue.Action>); + } + public final class SafetySourceStatus implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.safetycenter.SafetySourceStatus.IconAction getIconAction(); @@ -439,11 +486,15 @@ package android.safetycenter.config { } public final class SafetySource implements android.os.Parcelable { + method public boolean areNotificationsAllowed(); method public int describeContents(); + method @Nullable public String getDeduplicationGroup(); method @NonNull public String getId(); method public int getInitialDisplayState(); method @Nullable public String getIntentAction(); method public int getMaxSeverityLevel(); + method @Nullable public String getOptionalPackageName(); + method @NonNull public java.util.Set<java.lang.String> getPackageCertificateHashes(); method @NonNull public String getPackageName(); method public int getProfile(); method @StringRes public int getSearchTermsResId(); @@ -468,12 +519,15 @@ package android.safetycenter.config { public static final class SafetySource.Builder { ctor public SafetySource.Builder(int); + method @NonNull public android.safetycenter.config.SafetySource.Builder addPackageCertificateHash(@NonNull String); method @NonNull public android.safetycenter.config.SafetySource build(); + method @NonNull public android.safetycenter.config.SafetySource.Builder setDeduplicationGroup(@Nullable String); method @NonNull public android.safetycenter.config.SafetySource.Builder setId(@Nullable String); method @NonNull public android.safetycenter.config.SafetySource.Builder setInitialDisplayState(int); method @NonNull public android.safetycenter.config.SafetySource.Builder setIntentAction(@Nullable String); method @NonNull public android.safetycenter.config.SafetySource.Builder setLoggingAllowed(boolean); method @NonNull public android.safetycenter.config.SafetySource.Builder setMaxSeverityLevel(int); + method @NonNull public android.safetycenter.config.SafetySource.Builder setNotificationsAllowed(boolean); method @NonNull public android.safetycenter.config.SafetySource.Builder setPackageName(@Nullable String); method @NonNull public android.safetycenter.config.SafetySource.Builder setProfile(int); method @NonNull public android.safetycenter.config.SafetySource.Builder setRefreshOnPageOpenAllowed(boolean); @@ -493,9 +547,11 @@ package android.safetycenter.config { method public int getType(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.config.SafetySourcesGroup> CREATOR; - field public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0; // 0x0 + field @Deprecated public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0; // 0x0 field public static final int SAFETY_SOURCES_GROUP_TYPE_HIDDEN = 2; // 0x2 - field public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1; // 0x1 + field @Deprecated public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1; // 0x1 + field public static final int SAFETY_SOURCES_GROUP_TYPE_STATEFUL = 0; // 0x0 + field public static final int SAFETY_SOURCES_GROUP_TYPE_STATELESS = 1; // 0x1 field public static final int STATELESS_ICON_TYPE_NONE = 0; // 0x0 field public static final int STATELESS_ICON_TYPE_PRIVACY = 1; // 0x1 } @@ -508,6 +564,7 @@ package android.safetycenter.config { method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setStatelessIconType(int); method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setSummaryResId(@StringRes int); method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setTitleResId(@StringRes int); + method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setType(int); } } diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java index bd8831070..1b72073e0 100644 --- a/framework-s/java/android/app/role/RoleManager.java +++ b/framework-s/java/android/app/role/RoleManager.java @@ -163,6 +163,15 @@ public final class RoleManager { "android.app.role.DEVICE_POLICY_MANAGEMENT"; /** + * The name of the financed device kiosk role. + * + * @hide + */ + @SystemApi + public static final String ROLE_FINANCED_DEVICE_KIOSK = + "android.app.role.FINANCED_DEVICE_KIOSK"; + + /** * @hide */ @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP }) diff --git a/framework-s/java/android/app/role/TEST_MAPPING b/framework-s/java/android/app/role/TEST_MAPPING index f8f140dd7..ce53dca05 100644 --- a/framework-s/java/android/app/role/TEST_MAPPING +++ b/framework-s/java/android/app/role/TEST_MAPPING @@ -4,7 +4,24 @@ "name": "CtsRoleTestCases", "options": [ { - "include-filter": "android.app.role.cts.RoleManagerTest" + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ], + "mainline-presubmit": [ + { + "name": "CtsRoleTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677748): These two tests currently fails on R base image + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList" + }, + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" } ] } diff --git a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl index a00bfa19d..3290c221b 100644 --- a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl +++ b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl @@ -22,6 +22,7 @@ import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceErrorDetails; import android.safetycenter.config.SafetyCenterConfig; +import java.util.List; /** * AIDL Interface for communicating with the Safety Center, which consolidates UI for security and @@ -72,6 +73,12 @@ interface ISafetyCenterManager { /** Requests safety sources to set their latest SafetySourceData for Safety Center. */ void refreshSafetySources(int refreshReason, int userId); + /** + * Requests a specific subset of safety sources to set their latest SafetySourceData for + * Safety Center. + */ + void refreshSpecificSafetySources(int refreshReason, int userId, in List<String> safetySourceIds); + /** Returns the current SafetyCenterConfig, if available. */ SafetyCenterConfig getSafetyCenterConfig(); @@ -90,8 +97,7 @@ interface ISafetyCenterManager { int userId); /** - * Dismiss a Safety Center issue and prevent it from appearing in the Safety Center or affecting - * the overall safety status. + * Dismiss a Safety Center issue and prevent it affecting the overall safety status. */ void dismissSafetyCenterIssue(String issueId, int userId); diff --git a/framework-s/java/android/safetycenter/SafetyCenterData.java b/framework-s/java/android/safetycenter/SafetyCenterData.java index 9a6e7f0ec..6fac01b5f 100644 --- a/framework-s/java/android/safetycenter/SafetyCenterData.java +++ b/framework-s/java/android/safetycenter/SafetyCenterData.java @@ -17,6 +17,7 @@ package android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; @@ -28,6 +29,8 @@ import android.os.Parcelable; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -53,7 +56,16 @@ public final class SafetyCenterData implements Parcelable { in.createTypedArrayList(SafetyCenterEntryOrGroup.CREATOR); List<SafetyCenterStaticEntryGroup> staticEntryGroups = in.createTypedArrayList(SafetyCenterStaticEntryGroup.CREATOR); - return new SafetyCenterData(status, issues, entryOrGroups, staticEntryGroups); + + if (SdkLevel.isAtLeastU()) { + List<SafetyCenterIssue> dismissedIssues = + in.createTypedArrayList(SafetyCenterIssue.CREATOR); + return new SafetyCenterData( + status, issues, entryOrGroups, staticEntryGroups, dismissedIssues); + } else { + return new SafetyCenterData( + status, issues, entryOrGroups, staticEntryGroups); + } } @Override @@ -66,6 +78,7 @@ public final class SafetyCenterData implements Parcelable { @NonNull private final List<SafetyCenterIssue> mIssues; @NonNull private final List<SafetyCenterEntryOrGroup> mEntriesOrGroups; @NonNull private final List<SafetyCenterStaticEntryGroup> mStaticEntryGroups; + @NonNull private final List<SafetyCenterIssue> mDismissedIssues; /** Creates a {@link SafetyCenterData}. */ public SafetyCenterData( @@ -77,6 +90,25 @@ public final class SafetyCenterData implements Parcelable { mIssues = unmodifiableList(new ArrayList<>(requireNonNull(issues))); mEntriesOrGroups = unmodifiableList(new ArrayList<>(requireNonNull(entriesOrGroups))); mStaticEntryGroups = unmodifiableList(new ArrayList<>(requireNonNull(staticEntryGroups))); + mDismissedIssues = unmodifiableList(new ArrayList<>()); + } + + /** Creates a {@link SafetyCenterData}. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public SafetyCenterData( + @NonNull SafetyCenterStatus status, + @NonNull List<SafetyCenterIssue> issues, + @NonNull List<SafetyCenterEntryOrGroup> entriesOrGroups, + @NonNull List<SafetyCenterStaticEntryGroup> staticEntryGroups, + @NonNull List<SafetyCenterIssue> dismissedIssues) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mStatus = requireNonNull(status); + mIssues = unmodifiableList(new ArrayList<>(requireNonNull(issues))); + mEntriesOrGroups = unmodifiableList(new ArrayList<>(requireNonNull(entriesOrGroups))); + mStaticEntryGroups = unmodifiableList(new ArrayList<>(requireNonNull(staticEntryGroups))); + mDismissedIssues = unmodifiableList(new ArrayList<>(requireNonNull(dismissedIssues))); } /** Returns the overall {@link SafetyCenterStatus} of the Safety Center. */ @@ -106,6 +138,16 @@ public final class SafetyCenterData implements Parcelable { return mStaticEntryGroups; } + /** Returns the list of dismissed {@link SafetyCenterIssue} objects in the Safety Center. */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public List<SafetyCenterIssue> getDismissedIssues() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mDismissedIssues; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -114,12 +156,14 @@ public final class SafetyCenterData implements Parcelable { return Objects.equals(mStatus, that.mStatus) && Objects.equals(mIssues, that.mIssues) && Objects.equals(mEntriesOrGroups, that.mEntriesOrGroups) - && Objects.equals(mStaticEntryGroups, that.mStaticEntryGroups); + && Objects.equals(mStaticEntryGroups, that.mStaticEntryGroups) + && Objects.equals(mDismissedIssues, that.mDismissedIssues); } @Override public int hashCode() { - return Objects.hash(mStatus, mIssues, mEntriesOrGroups, mStaticEntryGroups); + return Objects.hash( + mStatus, mIssues, mEntriesOrGroups, mStaticEntryGroups, mDismissedIssues); } @Override @@ -133,6 +177,8 @@ public final class SafetyCenterData implements Parcelable { + mEntriesOrGroups + ", mStaticEntryGroups=" + mStaticEntryGroups + + ", mDismissedIssues=" + + mDismissedIssues + '}'; } @@ -147,5 +193,8 @@ public final class SafetyCenterData implements Parcelable { dest.writeTypedList(mIssues); dest.writeTypedList(mEntriesOrGroups); dest.writeTypedList(mStaticEntryGroups); + if (SdkLevel.isAtLeastU()) { + dest.writeTypedList(mDismissedIssues); + } } } diff --git a/framework-s/java/android/safetycenter/SafetyCenterIssue.java b/framework-s/java/android/safetycenter/SafetyCenterIssue.java index 6d52e025d..934eef808 100644 --- a/framework-s/java/android/safetycenter/SafetyCenterIssue.java +++ b/framework-s/java/android/safetycenter/SafetyCenterIssue.java @@ -17,6 +17,7 @@ package android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; @@ -29,10 +30,13 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.os.Parcel; import android.os.Parcelable; +import android.safetycenter.config.SafetySourcesGroup; import android.text.TextUtils; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -91,13 +95,19 @@ public final class SafetyCenterIssue implements Parcelable { CharSequence title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); CharSequence subtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); CharSequence summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - return new Builder(id, title, summary) - .setSubtitle(subtitle) - .setSeverityLevel(in.readInt()) - .setDismissible(in.readBoolean()) - .setShouldConfirmDismissal(in.readBoolean()) - .setActions(in.createTypedArrayList(Action.CREATOR)) - .build(); + SafetyCenterIssue.Builder builder = + new Builder(id, title, summary) + .setSubtitle(subtitle) + .setSeverityLevel(in.readInt()) + .setDismissible(in.readBoolean()) + .setShouldConfirmDismissal(in.readBoolean()) + .setActions(in.createTypedArrayList(Action.CREATOR)); + if (SdkLevel.isAtLeastU()) { + builder.setAttributionTitle( + TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)); + builder.setGroupId(in.readString()); + } + return builder.build(); } @Override @@ -114,6 +124,8 @@ public final class SafetyCenterIssue implements Parcelable { private final boolean mDismissible; private final boolean mShouldConfirmDismissal; @NonNull private final List<Action> mActions; + @Nullable private final CharSequence mAttributionTitle; + @Nullable private final String mGroupId; private SafetyCenterIssue( @NonNull String id, @@ -123,7 +135,9 @@ public final class SafetyCenterIssue implements Parcelable { @IssueSeverityLevel int severityLevel, boolean isDismissible, boolean shouldConfirmDismissal, - @NonNull List<Action> actions) { + @NonNull List<Action> actions, + @Nullable CharSequence attributionTitle, + @Nullable String groupId) { mId = id; mTitle = title; mSubtitle = subtitle; @@ -132,6 +146,8 @@ public final class SafetyCenterIssue implements Parcelable { mDismissible = isDismissible; mShouldConfirmDismissal = shouldConfirmDismissal; mActions = actions; + mAttributionTitle = attributionTitle; + mGroupId = groupId; } /** @@ -161,6 +177,24 @@ public final class SafetyCenterIssue implements Parcelable { return mSummary; } + /** + * Returns the attribution title of this issue, or {@code null} if it has none. + * + * <p>This is displayed in the UI and helps to attribute issue cards to a particular source. + * + * @throws UnsupportedOperationException if accessed from a version lower than {@link + * UPSIDE_DOWN_CAKE} + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public CharSequence getAttributionTitle() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException( + "Method not supported for versions lower than UPSIDE_DOWN_CAKE"); + } + return mAttributionTitle; + } + /** Returns the {@link IssueSeverityLevel} of this issue. */ @IssueSeverityLevel public int getSeverityLevel() { @@ -188,6 +222,26 @@ public final class SafetyCenterIssue implements Parcelable { return mActions; } + /** + * Returns the ID of the {@link SafetySourcesGroup} that this issue belongs to, or {@code null} + * if it has none. + * + * <p>This ID is used for displaying the issue on its corresponding subpage in the Safety Center + * UI. + * + * @throws UnsupportedOperationException if accessed from a version lower than {@link + * UPSIDE_DOWN_CAKE} + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public String getGroupId() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException( + "Method not supported for versions lower than UPSIDE_DOWN_CAKE"); + } + return mGroupId; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -200,7 +254,9 @@ public final class SafetyCenterIssue implements Parcelable { && TextUtils.equals(mTitle, that.mTitle) && TextUtils.equals(mSubtitle, that.mSubtitle) && TextUtils.equals(mSummary, that.mSummary) - && Objects.equals(mActions, that.mActions); + && Objects.equals(mActions, that.mActions) + && TextUtils.equals(mAttributionTitle, that.mAttributionTitle) + && Objects.equals(mGroupId, that.mGroupId); } @Override @@ -213,7 +269,9 @@ public final class SafetyCenterIssue implements Parcelable { mSeverityLevel, mDismissible, mShouldConfirmDismissal, - mActions); + mActions, + mAttributionTitle, + mGroupId); } @Override @@ -235,6 +293,10 @@ public final class SafetyCenterIssue implements Parcelable { + mShouldConfirmDismissal + ", mActions=" + mActions + + ", mAttributionTitle=" + + mAttributionTitle + + ", mGroupId=" + + mGroupId + '}'; } @@ -253,6 +315,10 @@ public final class SafetyCenterIssue implements Parcelable { dest.writeBoolean(mDismissible); dest.writeBoolean(mShouldConfirmDismissal); dest.writeTypedList(mActions); + if (SdkLevel.isAtLeastU()) { + TextUtils.writeToParcel(mAttributionTitle, dest, flags); + dest.writeString(mGroupId); + } } /** Builder class for {@link SafetyCenterIssue}. */ @@ -266,6 +332,8 @@ public final class SafetyCenterIssue implements Parcelable { private boolean mDismissible = true; private boolean mShouldConfirmDismissal = true; private List<Action> mActions = new ArrayList<>(); + @Nullable private CharSequence mAttributionTitle; + @Nullable private String mGroupId; /** * Creates a {@link Builder} for a {@link SafetyCenterIssue}. @@ -291,6 +359,8 @@ public final class SafetyCenterIssue implements Parcelable { mDismissible = issue.mDismissible; mShouldConfirmDismissal = issue.mShouldConfirmDismissal; mActions = new ArrayList<>(issue.mActions); + mAttributionTitle = issue.mAttributionTitle; + mGroupId = issue.mGroupId; } /** Sets the ID for this issue. */ @@ -322,6 +392,25 @@ public final class SafetyCenterIssue implements Parcelable { } /** + * Sets or clears the optional attribution title for this issue. + * + * <p>This is displayed in the UI and helps to attribute issue cards to a particular source. + * + * @throws UnsupportedOperationException if accessed from a version lower than {@link + * UPSIDE_DOWN_CAKE} + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException( + "Method not supported for versions lower than UPSIDE_DOWN_CAKE"); + } + mAttributionTitle = attributionTitle; + return this; + } + + /** * Sets {@link IssueSeverityLevel} for this issue. Defaults to {@link * #ISSUE_SEVERITY_LEVEL_OK}. */ @@ -357,6 +446,27 @@ public final class SafetyCenterIssue implements Parcelable { return this; } + /** + * Sets the ID of {@link SafetySourcesGroup} that this issue belongs to. Defaults to a + * {@code null} value. + * + * <p>This ID is used for displaying the issue on its corresponding subpage in the Safety + * Center UI. + * + * @throws UnsupportedOperationException if accessed from a version lower than {@link + * UPSIDE_DOWN_CAKE} + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setGroupId(@Nullable String groupId) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException( + "Method not supported for versions lower than UPSIDE_DOWN_CAKE"); + } + mGroupId = groupId; + return this; + } + /** Creates the {@link SafetyCenterIssue} defined by this {@link Builder}. */ @NonNull public SafetyCenterIssue build() { @@ -368,7 +478,9 @@ public final class SafetyCenterIssue implements Parcelable { mSeverityLevel, mDismissible, mShouldConfirmDismissal, - unmodifiableList(new ArrayList<>(mActions))); + unmodifiableList(new ArrayList<>(mActions)), + mAttributionTitle, + mGroupId); } } diff --git a/framework-s/java/android/safetycenter/SafetyCenterManager.java b/framework-s/java/android/safetycenter/SafetyCenterManager.java index acee6112e..bb67f578f 100644 --- a/framework-s/java/android/safetycenter/SafetyCenterManager.java +++ b/framework-s/java/android/safetycenter/SafetyCenterManager.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS; import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE; import static android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Objects.requireNonNull; @@ -32,6 +33,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.os.Binder; @@ -42,9 +44,11 @@ import android.util.ArrayMap; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; +import com.android.modules.utils.build.SdkLevel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.Map; import java.util.concurrent.Executor; @@ -195,12 +199,21 @@ public final class SafetyCenterManager { * disambiguate personal profile vs. managed profiles issues). * * <p>This extra can be used in conjunction with {@link #EXTRA_SAFETY_SOURCE_ISSUE_ID} and - * {@link #EXTRA_SAFETY_SOURCE_ID}. Otherwise, no redirection will occur. + * {@link #EXTRA_SAFETY_SOURCE_ID}. Otherwise, the device's primary user will be used. */ public static final String EXTRA_SAFETY_SOURCE_USER_HANDLE = "android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE"; /** + * Used as a {@code String} extra field in {@link Intent#ACTION_SAFETY_CENTER} intents to + * specify the ID for a group of safety sources. If applicable, this will redirect to the + * group's corresponding subpage in the UI. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final String EXTRA_SAFETY_SOURCES_GROUP_ID = + "android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID"; + + /** * Used as an int value for {@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE} to indicate that * the safety source should fetch fresh data relating to their safety state upon receiving a * broadcast with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES} and provide it to Safety @@ -214,7 +227,7 @@ public final class SafetyCenterManager { /** * Used as an int value for {@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE} to indicate that - * upon receiving a broadcasts with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES}, the + * upon receiving a broadcast with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES}, the * safety source should provide data relating to their safety state to Safety Center. * * <p>If the source already has its safety data cached, it may provide it without triggering a @@ -255,6 +268,10 @@ public final class SafetyCenterManager { /** Indicates a generic reason for Safety Center refresh. */ public static final int REFRESH_REASON_OTHER = 600; + /** Indicates a periodic background refresh. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int REFRESH_REASON_PERIODIC = 700; + /** * The reason for requesting a refresh of {@link SafetySourceData} from safety sources. * @@ -268,9 +285,11 @@ public final class SafetyCenterManager { REFRESH_REASON_DEVICE_REBOOT, REFRESH_REASON_DEVICE_LOCALE_CHANGE, REFRESH_REASON_SAFETY_CENTER_ENABLED, - REFRESH_REASON_OTHER + REFRESH_REASON_OTHER, + REFRESH_REASON_PERIODIC }) @Retention(RetentionPolicy.SOURCE) + @TargetApi(UPSIDE_DOWN_CAKE) public @interface RefreshReason {} /** Listener for changes to {@link SafetyCenterData}. */ @@ -429,6 +448,42 @@ public final class SafetyCenterManager { } } + /** + * Requests a specific subset of safety sources to set their latest {@link SafetySourceData} for + * Safety Center. + * + * <p>This API sends a broadcast to safety sources with action {@link + * #ACTION_REFRESH_SAFETY_SOURCES} and {@link #EXTRA_REFRESH_SAFETY_SOURCE_IDS} to specify the + * IDs of safety sources being requested for data by Safety Center. + * + * <p>This API is an overload of {@link #refreshSafetySources(int)} and is used to request data + * from safety sources that are part of a subpage in the Safety Center UI. + * + * @see #refreshSafetySources(int) + * @param refreshReason the reason for the refresh + * @param safetySourceIds list of IDs for the safety sources being refreshed + * @throws UnsupportedOperationException if accessed from a version lower than {@link + * UPSIDE_DOWN_CAKE} + */ + @RequiresPermission(MANAGE_SAFETY_CENTER) + @RequiresApi(UPSIDE_DOWN_CAKE) + public void refreshSafetySources( + @RefreshReason int refreshReason, @NonNull List<String> safetySourceIds) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException( + "Method not supported for versions lower than UPSIDE_DOWN_CAKE"); + } + + requireNonNull(safetySourceIds, "safetySourceIds cannot be null"); + + try { + mService.refreshSpecificSafetySources( + refreshReason, mContext.getUser().getIdentifier(), safetySourceIds); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** Returns the current {@link SafetyCenterConfig}, if available. */ @RequiresPermission(MANAGE_SAFETY_CENTER) @Nullable @@ -507,8 +562,7 @@ public final class SafetyCenterManager { } /** - * Dismiss a Safety Center issue and prevent it from appearing in the Safety Center or affecting - * the overall safety status. + * Dismiss a Safety Center issue and prevent it from affecting the overall safety status. * * @param safetyCenterIssueId the target issue ID returned by {@link SafetyCenterIssue#getId()} */ diff --git a/framework-s/java/android/safetycenter/SafetySourceIssue.java b/framework-s/java/android/safetycenter/SafetySourceIssue.java index cf64818b9..521fe1362 100644 --- a/framework-s/java/android/safetycenter/SafetySourceIssue.java +++ b/framework-s/java/android/safetycenter/SafetySourceIssue.java @@ -17,6 +17,7 @@ package android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static com.android.internal.util.Preconditions.checkArgument; @@ -28,13 +29,17 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TargetApi; import android.app.PendingIntent; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -64,9 +69,26 @@ public final class SafetySourceIssue implements Parcelable { /** Indicates that the risk associated with the issue is related to a user's account safety. */ public static final int ISSUE_CATEGORY_ACCOUNT = 200; - /** Indicates that the risk associated with the issue is related to a user's general safety. */ + /** + * Indicates that the risk associated with the issue is related to a user's general safety. + * + * <p>This is the default. It is a generic value used when the category is not known or is not + * relevant. + */ public static final int ISSUE_CATEGORY_GENERAL = 300; + /** Indicates that the risk associated with the issue is related to a user's data. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_CATEGORY_DATA = 400; + + /** Indicates that the risk associated with the issue is related to a user's passwords. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_CATEGORY_PASSWORDS = 500; + + /** Indicates that the risk associated with the issue is related to a user's personal safety. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_CATEGORY_PERSONAL_SAFETY = 600; + /** * All possible issue categories. * @@ -84,10 +106,108 @@ public final class SafetySourceIssue implements Parcelable { ISSUE_CATEGORY_DEVICE, ISSUE_CATEGORY_ACCOUNT, ISSUE_CATEGORY_GENERAL, + ISSUE_CATEGORY_DATA, + ISSUE_CATEGORY_PASSWORDS, + ISSUE_CATEGORY_PERSONAL_SAFETY }) @Retention(RetentionPolicy.SOURCE) + @TargetApi(UPSIDE_DOWN_CAKE) public @interface IssueCategory {} + /** Value signifying that the source has not specified a particular notification behavior. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int NOTIFICATION_BEHAVIOR_UNSPECIFIED = 0; + + /** An issue which Safety Center should never notify the user about. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int NOTIFICATION_BEHAVIOR_NEVER = 100; + + /** + * An issue which Safety Center may notify the user about after a delay if it has not been + * resolved. Safety Center does not provide any guarantee about the duration of the delay. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int NOTIFICATION_BEHAVIOR_DELAYED = 200; + + /** An issue which Safety Center may notify the user about immediately. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int NOTIFICATION_BEHAVIOR_IMMEDIATELY = 300; + + /** + * All possible notification behaviors. + * + * <p>The notification behavior of a {@link SafetySourceIssue} determines if and when Safety + * Center should notify the user about it. + * + * @hide + * @see Builder#setNotificationBehavior(int) + */ + @IntDef( + prefix = {"NOTIFICATION_BEHAVIOR_"}, + value = { + NOTIFICATION_BEHAVIOR_UNSPECIFIED, + NOTIFICATION_BEHAVIOR_NEVER, + NOTIFICATION_BEHAVIOR_DELAYED, + NOTIFICATION_BEHAVIOR_IMMEDIATELY + }) + @Retention(RetentionPolicy.SOURCE) + @TargetApi(UPSIDE_DOWN_CAKE) + public @interface NotificationBehavior {} + + /** + * An issue which requires manual user input to be resolved. + * + * <p>This is the default. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_ACTIONABILITY_MANUAL = 0; + + /** + * An issue which is just a "tip" and may not require any user input. + * + * <p>It is still possible to provide {@link Action}s to e.g. "learn more" about it or + * acknowledge it. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_ACTIONABILITY_TIP = 100; + + /** + * An issue which has already been actioned and may not require any user input. + * + * <p>It is still possible to provide {@link Action}s to e.g. "learn more" about it or + * acknowledge it. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final int ISSUE_ACTIONABILITY_AUTOMATIC = 200; + + /** + * All possible issue actionability. + * + * <p>An issue's actionability represent what action is expected from the user as a result of + * showing them this issue. + * + * <p>If the user needs to manually resolve it; this is typically achieved using an {@link + * Action} (e.g. by resolving the issue directly through the Safety Center screen, or by + * navigating to another page). + * + * <p>If the issue does not need to be resolved manually by the user, it is possible not to + * provide any {@link Action}. However, this may still be desirable to e.g. to "learn more" + * about it or acknowledge it. + * + * @hide + * @see Builder#setIssueActionability(int) + */ + @IntDef( + prefix = {"ISSUE_ACTIONABILITY_"}, + value = { + ISSUE_ACTIONABILITY_MANUAL, + ISSUE_ACTIONABILITY_TIP, + ISSUE_ACTIONABILITY_AUTOMATIC + }) + @Retention(RetentionPolicy.SOURCE) + @TargetApi(UPSIDE_DOWN_CAKE) + public @interface IssueActionability {} + @NonNull public static final Creator<SafetySourceIssue> CREATOR = new Creator<SafetySourceIssue>() { @@ -111,6 +231,14 @@ public final class SafetySourceIssue implements Parcelable { for (int i = 0; i < actions.size(); i++) { builder.addAction(actions.get(i)); } + if (SdkLevel.isAtLeastU()) { + builder.setCustomNotification(in.readTypedObject(Notification.CREATOR)); + builder.setNotificationBehavior(in.readInt()); + builder.setAttributionTitle( + TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)); + builder.setDeduplicationId(in.readString()); + builder.setIssueActionability(in.readInt()); + } return builder.build(); } @@ -129,6 +257,11 @@ public final class SafetySourceIssue implements Parcelable { @Nullable private final PendingIntent mOnDismissPendingIntent; @IssueCategory private final int mIssueCategory; @NonNull private final String mIssueTypeId; + @Nullable private final Notification mCustomNotification; + @NotificationBehavior private final int mNotificationBehavior; + @Nullable private final CharSequence mAttributionTitle; + @Nullable private final String mDeduplicationId; + @IssueActionability private final int mIssueActionability; private SafetySourceIssue( @NonNull String id, @@ -139,7 +272,12 @@ public final class SafetySourceIssue implements Parcelable { @IssueCategory int issueCategory, @NonNull List<Action> actions, @Nullable PendingIntent onDismissPendingIntent, - @NonNull String issueTypeId) { + @NonNull String issueTypeId, + @Nullable Notification customNotification, + @NotificationBehavior int notificationBehavior, + @Nullable CharSequence attributionTitle, + @Nullable String deduplicationId, + @IssueActionability int issueActionability) { this.mId = id; this.mTitle = title; this.mSubtitle = subtitle; @@ -149,6 +287,11 @@ public final class SafetySourceIssue implements Parcelable { this.mActions = actions; this.mOnDismissPendingIntent = onDismissPendingIntent; this.mIssueTypeId = issueTypeId; + this.mCustomNotification = customNotification; + this.mNotificationBehavior = notificationBehavior; + this.mAttributionTitle = attributionTitle; + this.mDeduplicationId = deduplicationId; + this.mIssueActionability = issueActionability; } /** @@ -183,6 +326,22 @@ public final class SafetySourceIssue implements Parcelable { return mSummary; } + /** + * Returns the localized attribution title of the issue to be displayed in the UI. + * + * <p>This is displayed in the UI and helps to attribute issue cards to a particular source. If + * this value is {@code null}, the title of the group that contains the Safety Source will be + * used. + */ + @Nullable + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public CharSequence getAttributionTitle() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mAttributionTitle; + } + /** Returns the {@link SafetySourceData.SeverityLevel} of the issue. */ @SafetySourceData.SeverityLevel public int getSeverityLevel() { @@ -243,6 +402,114 @@ public final class SafetySourceIssue implements Parcelable { return mIssueTypeId; } + /** + * Returns the optional custom {@link Notification} for this issue which overrides the title, + * text and actions for any {@link android.app.Notification} generated for this {@link + * SafetySourceIssue}. + * + * <p>Safety Center may still generate a default notification from the other details of this + * issue when no custom notification has been set. See {@link #getNotificationBehavior()} for + * details + * + * @see Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification + * @see #getNotificationBehavior() + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public Notification getCustomNotification() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mCustomNotification; + } + + /** + * Returns the {@link NotificationBehavior} for this issue which determines if and when Safety + * Center will post a notification for this issue. + * + * <p>Any notification will be based on the {@link #getCustomNotification()} if set, or the + * other properties of this issue otherwise. + * + * <ul> + * <li>If {@link #NOTIFICATION_BEHAVIOR_IMMEDIATELY} then Safety Center will immediately + * create and post a notification + * <li>If {@link #NOTIFICATION_BEHAVIOR_DELAYED} then a notification will only be posted after + * a delay, if this issue has not been resolved. + * <li>If {@link #NOTIFICATION_BEHAVIOR_UNSPECIFIED} then a notification may or may not be + * posted, the exact behavior is defined by Safety Center. + * <li>If {@link #NOTIFICATION_BEHAVIOR_NEVER} Safety Center will never post a notification + * about this issue. Sources should specify this behavior when they wish to handle their + * own notifications. When this behavior is set sources should not set a custom + * notification. + * </ul> + * + * @see Builder#setNotificationBehavior(int) + */ + @NotificationBehavior + @RequiresApi(UPSIDE_DOWN_CAKE) + public int getNotificationBehavior() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mNotificationBehavior; + } + + /** + * Returns the identifier used to deduplicate this issue against other issues with the same + * deduplication identifiers. + * + * <p>Deduplication identifier will be used to identify duplicate issues. This identifier + * applies across all safety sources which are part of the same deduplication group. + * Deduplication groups can be set, for each source, in the SafetyCenter config. Therefore, two + * issues are considered duplicate if their sources are part of the same deduplication group and + * they have the same deduplication identifier. + * + * <p>Out of all issues that are found to be duplicates, only one will be shown in the UI (the + * one with the highest severity, or in case of same severities, the one placed highest in the + * config). + * + * <p>Expected usage implies different sources will coordinate to set the same deduplication + * identifiers on issues that they want to deduplicate. + * + * <p>This shouldn't be a default mechanism for deduplication of issues. Most of the time + * sources should coordinate or communicate to only send the issue from one of them. That would + * also allow sources to choose which one will be displaying the issue, instead of depending on + * severity and config order. This API should only be needed if for some reason this isn't + * possible, for example, when sources can't communicate with each other and/or send issues at + * different times and/or issues can be of different severities. + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public String getDeduplicationId() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mDeduplicationId; + } + + /** + * Returns the {@link IssueActionability} for this issue which determines what type of action is + * required from the user: + * + * <ul> + * <li>If {@link #ISSUE_ACTIONABILITY_MANUAL} then user input is required to resolve the issue + * <li>If {@link #ISSUE_ACTIONABILITY_TIP} then the user needs to review this issue as a tip + * to improve their overall safety, and possibly acknowledge it + * <li>If {@link #ISSUE_ACTIONABILITY_AUTOMATIC} then the user needs to review this issue as + * something that has been resolved on their behalf, and possibly acknowledge it + * </ul> + * + * @see Builder#setIssueActionability(int) + */ + @IssueActionability + @RequiresApi(UPSIDE_DOWN_CAKE) + public int getIssueActionability() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mIssueActionability; + } + @Override public int describeContents() { return 0; @@ -259,6 +526,13 @@ public final class SafetySourceIssue implements Parcelable { dest.writeTypedList(mActions); dest.writeTypedObject(mOnDismissPendingIntent, flags); dest.writeString(mIssueTypeId); + if (SdkLevel.isAtLeastU()) { + dest.writeTypedObject(mCustomNotification, flags); + dest.writeInt(mNotificationBehavior); + TextUtils.writeToParcel(mAttributionTitle, dest, flags); + dest.writeString(mDeduplicationId); + dest.writeInt(mIssueActionability); + } } @Override @@ -274,7 +548,12 @@ public final class SafetySourceIssue implements Parcelable { && mIssueCategory == that.mIssueCategory && mActions.equals(that.mActions) && Objects.equals(mOnDismissPendingIntent, that.mOnDismissPendingIntent) - && TextUtils.equals(mIssueTypeId, that.mIssueTypeId); + && TextUtils.equals(mIssueTypeId, that.mIssueTypeId) + && Objects.equals(mCustomNotification, that.mCustomNotification) + && mNotificationBehavior == that.mNotificationBehavior + && TextUtils.equals(mAttributionTitle, that.mAttributionTitle) + && TextUtils.equals(mDeduplicationId, that.mDeduplicationId) + && mIssueActionability == that.mIssueActionability; } @Override @@ -288,7 +567,12 @@ public final class SafetySourceIssue implements Parcelable { mIssueCategory, mActions, mOnDismissPendingIntent, - mIssueTypeId); + mIssueTypeId, + mCustomNotification, + mNotificationBehavior, + mAttributionTitle, + mDeduplicationId, + mIssueActionability); } @Override @@ -312,6 +596,16 @@ public final class SafetySourceIssue implements Parcelable { + mOnDismissPendingIntent + ", mIssueTypeId=" + mIssueTypeId + + ", mCustomNotification=" + + mCustomNotification + + ", mNotificationBehavior=" + + mNotificationBehavior + + ", mAttributionTitle=" + + mAttributionTitle + + ", mDeduplicationId=" + + mDeduplicationId + + ", mIssueActionability=" + + mIssueActionability + '}'; } @@ -349,6 +643,18 @@ public final class SafetySourceIssue implements Parcelable { } }; + private static void enforceUniqueActionIds( + @NonNull List<SafetySourceIssue.Action> actions, @NonNull String message) { + Set<String> actionIds = new HashSet<>(); + for (int i = 0; i < actions.size(); i++) { + SafetySourceIssue.Action action = actions.get(i); + + String actionId = action.getId(); + checkArgument(!actionIds.contains(actionId), message); + actionIds.add(actionId); + } + } + @NonNull private final String mId; @NonNull private final CharSequence mLabel; @NonNull private final PendingIntent mPendingIntent; @@ -511,6 +817,176 @@ public final class SafetySourceIssue implements Parcelable { } } + /** + * Data for Safety Center to use when constructing a system {@link android.app.Notification} + * about a related {@link SafetySourceIssue}. + * + * <p>Safety Center can construct a default notification for any issue, but sources may use + * {@link Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification)} if + * they want to override the title, text or actions. + * + * @see #getCustomNotification() + * @see Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification) + * @see #getNotificationBehavior() + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public static final class Notification implements Parcelable { + + @NonNull + public static final Creator<Notification> CREATOR = + new Creator<Notification>() { + @Override + public Notification createFromParcel(Parcel in) { + return new Builder( + TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in), + TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)) + .setActions(in.createTypedArrayList(Action.CREATOR)) + .build(); + } + + @Override + public Notification[] newArray(int size) { + return new Notification[size]; + } + }; + + @NonNull private final CharSequence mTitle; + @NonNull private final CharSequence mText; + @NonNull private final List<Action> mActions; + + private Notification( + @NonNull CharSequence title, + @NonNull CharSequence text, + @NonNull List<Action> actions) { + mTitle = title; + mText = text; + mActions = actions; + } + + /** + * Custom title which will be used instead of {@link SafetySourceIssue#getTitle()} when + * building a {@link android.app.Notification} for this issue. + */ + @NonNull + public CharSequence getTitle() { + return mTitle; + } + + /** + * Custom text which will be used instead of {@link SafetySourceIssue#getSummary()} when + * building a {@link android.app.Notification} for this issue. + */ + @NonNull + public CharSequence getText() { + return mText; + } + + /** + * Custom list of {@link Action} instances which will be used instead of {@link + * SafetySourceIssue#getActions()} when building a {@link android.app.Notification} for this + * issue. + * + * <p>If this list is empty then the resulting {@link android.app.Notification} will have + * zero action buttons. + */ + @NonNull + public List<Action> getActions() { + return mActions; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + TextUtils.writeToParcel(mTitle, dest, flags); + TextUtils.writeToParcel(mText, dest, flags); + dest.writeTypedList(mActions); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Notification)) return false; + Notification that = (Notification) o; + return TextUtils.equals(mTitle, that.mTitle) + && TextUtils.equals(mText, that.mText) + && mActions.equals(that.mActions); + } + + @Override + public int hashCode() { + return Objects.hash(mTitle, mText, mActions); + } + + @Override + public String toString() { + return "Notification{" + + "mTitle=" + + mTitle + + ", mText=" + + mText + + ", mActions=" + + mActions + + '}'; + } + + /** Builder for {@link SafetySourceIssue.Notification}. */ + public static final class Builder { + + @NonNull private final CharSequence mTitle; + @NonNull private final CharSequence mText; + @NonNull private final List<Action> mActions = new ArrayList<>(); + + public Builder(@NonNull CharSequence title, @NonNull CharSequence text) { + mTitle = requireNonNull(title); + mText = requireNonNull(text); + } + + /** Adds an {@link Action} to be show on the custom {@link Notification}. */ + @NonNull + public Builder addAction(@NonNull Action action) { + mActions.add(requireNonNull(action)); + return this; + } + + /** + * Sets the list of {@link Action}s to be show on the custom {@link Notification}, + * removing any which were previously added. + */ + @NonNull + public Builder setActions(@NonNull List<Action> actions) { + mActions.clear(); + mActions.addAll(requireNonNull(actions)); + return this; + } + + /** + * Clears all the {@link Action}s that were added to this custom {@link + * Notification.Builder}. + */ + @NonNull + public Builder clearActions() { + mActions.clear(); + return this; + } + + /** Builds a {@link Notification} instance. */ + @NonNull + public Notification build() { + List<Action> actions = unmodifiableList(new ArrayList<>(mActions)); + Action.enforceUniqueActionIds( + actions, "Custom notification cannot have duplicate action ids"); + checkArgument( + actions.size() <= 2, + "Custom notification must not contain more than 2 actions"); + return new Notification(mTitle, mText, actions); + } + } + } + /** Builder class for {@link SafetySourceIssue}. */ public static final class Builder { @@ -524,6 +1000,18 @@ public final class SafetySourceIssue implements Parcelable { @Nullable private CharSequence mSubtitle; @IssueCategory private int mIssueCategory = ISSUE_CATEGORY_GENERAL; @Nullable private PendingIntent mOnDismissPendingIntent; + @Nullable private CharSequence mAttributionTitle; + @Nullable private String mDeduplicationId; + + @Nullable private Notification mCustomNotification = null; + + @SuppressLint("NewApi") + @NotificationBehavior + private int mNotificationBehavior = NOTIFICATION_BEHAVIOR_UNSPECIFIED; + + @SuppressLint("NewApi") + @IssueActionability + private int mIssueActionability = ISSUE_ACTIONABILITY_MANUAL; /** Creates a {@link Builder} for a {@link SafetySourceIssue}. */ public Builder( @@ -547,6 +1035,23 @@ public final class SafetySourceIssue implements Parcelable { } /** + * Sets or clears the optional attribution title for this issue. + * + * <p>This is displayed in the UI and helps to attribute an issue to a particular source. If + * this value is {@code null}, the title of the group that contains the Safety Source will + * be used. + */ + @NonNull + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mAttributionTitle = attributionTitle; + return this; + } + + /** * Sets the category of the risk associated with the issue. * * <p>The default category will be {@link #ISSUE_CATEGORY_GENERAL}. @@ -590,12 +1095,98 @@ public final class SafetySourceIssue implements Parcelable { return this; } + /** + * Sets a custom {@link Notification} for this issue. + * + * <p>Using a custom {@link Notification} a source may specify a different {@link + * Notification#getTitle()}, {@link Notification#getText()} and {@link + * Notification#getActions()} for Safety Center to use when constructing a notification for + * this issue. + * + * <p>Safety Center may still generate a default notification from the other details of this + * issue when no custom notification has been set, depending on the issue's {@link + * #getNotificationBehavior()}. + * + * @see #getCustomNotification() + * @see #setNotificationBehavior(int) + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setCustomNotification(@Nullable Notification customNotification) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mCustomNotification = customNotification; + return this; + } + + /** + * Sets the notification behavior of the issue. + * + * <p>Must be one of {@link #NOTIFICATION_BEHAVIOR_UNSPECIFIED}, {@link + * #NOTIFICATION_BEHAVIOR_NEVER}, {@link #NOTIFICATION_BEHAVIOR_DELAYED} or {@link + * #NOTIFICATION_BEHAVIOR_IMMEDIATELY}. See {@link #getNotificationBehavior()} for details + * of how Safety Center will interpret each of these. + * + * @see #getNotificationBehavior() + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setNotificationBehavior(@NotificationBehavior int notificationBehavior) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mNotificationBehavior = validateNotificationBehavior(notificationBehavior); + return this; + } + + /** + * Sets the deduplication identifier for the issue. + * + * @see #getDeduplicationId() + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setDeduplicationId(@Nullable String deduplicationId) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mDeduplicationId = deduplicationId; + return this; + } + + /** + * Sets the issue actionability of the issue. + * + * <p>Must be one of {@link #ISSUE_ACTIONABILITY_MANUAL} (default), {@link + * #ISSUE_ACTIONABILITY_TIP}, {@link #ISSUE_ACTIONABILITY_AUTOMATIC}. + * + * @see #getIssueActionability() + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setIssueActionability(@IssueActionability int issueActionability) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mIssueActionability = validateIssueActionability(issueActionability); + return this; + } + /** Creates the {@link SafetySourceIssue} defined by this {@link Builder}. */ @NonNull public SafetySourceIssue build() { List<SafetySourceIssue.Action> actions = unmodifiableList(new ArrayList<>(mActions)); - enforceUniqueActionIds(actions); - checkArgument(!actions.isEmpty(), "Safety source issue must contain at least 1 action"); + Action.enforceUniqueActionIds( + actions, "Safety source issue cannot have duplicate action ids"); + if (SdkLevel.isAtLeastU()) { + checkArgument( + mIssueActionability != ISSUE_ACTIONABILITY_MANUAL || !actions.isEmpty(), + "Actionable safety source issue must contain at least 1 action"); + } else { + checkArgument( + !actions.isEmpty(), "Safety source issue must contain at least 1 action"); + } checkArgument( actions.size() <= 2, "Safety source issue must not contain more than 2 actions"); @@ -608,21 +1199,12 @@ public final class SafetySourceIssue implements Parcelable { mIssueCategory, actions, mOnDismissPendingIntent, - mIssueTypeId); - } - - private static void enforceUniqueActionIds( - @NonNull List<SafetySourceIssue.Action> actions) { - Set<String> actionIds = new HashSet<>(); - for (int i = 0; i < actions.size(); i++) { - SafetySourceIssue.Action action = actions.get(i); - - String actionId = action.getId(); - checkArgument( - !actionIds.contains(actionId), - "Safety source issue cannot have duplicate action ids"); - actionIds.add(actionId); - } + mIssueTypeId, + mCustomNotification, + mNotificationBehavior, + mAttributionTitle, + mDeduplicationId, + mIssueActionability); } } @@ -652,7 +1234,43 @@ public final class SafetySourceIssue implements Parcelable { return value; default: } + if (SdkLevel.isAtLeastU()) { + switch (value) { + case ISSUE_CATEGORY_DATA: + case ISSUE_CATEGORY_PASSWORDS: + case ISSUE_CATEGORY_PERSONAL_SAFETY: + return value; + default: + } + } throw new IllegalArgumentException( "Unexpected IssueCategory for SafetySourceIssue: " + value); } + + @NotificationBehavior + private static int validateNotificationBehavior(int value) { + switch (value) { + case NOTIFICATION_BEHAVIOR_UNSPECIFIED: + case NOTIFICATION_BEHAVIOR_NEVER: + case NOTIFICATION_BEHAVIOR_DELAYED: + case NOTIFICATION_BEHAVIOR_IMMEDIATELY: + return value; + default: + } + throw new IllegalArgumentException( + "Unexpected NotificationBehavior for SafetySourceIssue: " + value); + } + + @IssueActionability + private static int validateIssueActionability(int value) { + switch (value) { + case ISSUE_ACTIONABILITY_MANUAL: + case ISSUE_ACTIONABILITY_TIP: + case ISSUE_ACTIONABILITY_AUTOMATIC: + return value; + default: + } + throw new IllegalArgumentException( + "Unexpected IssueActionability for SafetySourceIssue: " + value); + } } diff --git a/framework-s/java/android/safetycenter/TEST_MAPPING b/framework-s/java/android/safetycenter/TEST_MAPPING index e2eb5ef5d..c702ee852 100644 --- a/framework-s/java/android/safetycenter/TEST_MAPPING +++ b/framework-s/java/android/safetycenter/TEST_MAPPING @@ -24,5 +24,15 @@ { "name": "SafetyCenterFunctionalTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/framework-s/java/android/safetycenter/config/BuilderUtils.java b/framework-s/java/android/safetycenter/config/BuilderUtils.java index e0e08183d..35ec39cc5 100644 --- a/framework-s/java/android/safetycenter/config/BuilderUtils.java +++ b/framework-s/java/android/safetycenter/config/BuilderUtils.java @@ -25,6 +25,7 @@ import android.content.res.Resources; import androidx.annotation.RequiresApi; +import java.util.Collection; import java.util.Objects; @RequiresApi(TIRAMISU) @@ -39,12 +40,12 @@ final class BuilderUtils { boolean prohibited, @Nullable Object defaultValue) { if (attribute == null && required) { - throw new IllegalStateException("Required attribute " + name + " missing"); + throwRequiredAttributeMissing(name); } boolean nonDefaultValueProvided = !Objects.equals(attribute, defaultValue); boolean checkProhibited = prohibited && nonDefaultValueProvided; if (attribute != null && checkProhibited) { - throw new IllegalStateException("Prohibited attribute " + name + " present"); + throwProhibitedAttributePresent(name); } } @@ -67,7 +68,7 @@ final class BuilderUtils { return Resources.ID_NULL; } if (required && value == Resources.ID_NULL) { - throw new IllegalStateException("Required attribute " + name + " invalid"); + throwRequiredAttributeInvalid(name); } return value; } @@ -119,4 +120,37 @@ final class BuilderUtils { } return value; } + + /** + * Validates a collection argument from a builder. + * + * <ul> + * <li>If {@code required}, a non-empty collection must be supplied. + * <li>If {@code prohibited}, an empty collection must be supplied. + * </ul> + */ + static <T> void validateCollection( + @NonNull Collection<T> value, + @NonNull String name, + boolean required, + boolean prohibited) { + if (value.isEmpty() && required) { + throwRequiredAttributeMissing(name); + } + if (!value.isEmpty() && prohibited) { + throwProhibitedAttributePresent(name); + } + } + + static void throwRequiredAttributeMissing(@NonNull String attribute) { + throw new IllegalStateException("Required attribute " + attribute + " missing"); + } + + static void throwProhibitedAttributePresent(@NonNull String attribute) { + throw new IllegalStateException("Prohibited attribute " + attribute + " present"); + } + + static void throwRequiredAttributeInvalid(@NonNull String attribute) { + throw new IllegalStateException("Required attribute " + attribute + " invalid"); + } } diff --git a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java index 9c8691e44..f5d43970c 100644 --- a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java +++ b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java @@ -129,8 +129,8 @@ public final class SafetyCenterConfig implements Parcelable { /** * Creates the {@link SafetyCenterConfig} defined by this {@link Builder}. * - * <p>Throws an {@link IllegalStateException} if any constraint on the Safety Center - * configuration is violated. + * @throws IllegalStateException if any constraint on the Safety Center configuration is + * violated */ @NonNull public SafetyCenterConfig build() { diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java index e5008bfea..6c4c5b766 100644 --- a/framework-s/java/android/safetycenter/config/SafetySource.java +++ b/framework-s/java/android/safetycenter/config/SafetySource.java @@ -17,6 +17,7 @@ package android.safetycenter.config; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import android.annotation.IntDef; import android.annotation.NonNull; @@ -26,12 +27,17 @@ import android.annotation.SystemApi; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArraySet; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.Objects; +import java.util.Set; /** * Data class used to represent the initial configuration of a safety source. @@ -152,20 +158,29 @@ public final class SafetySource implements Parcelable { @Override public SafetySource createFromParcel(Parcel in) { int type = in.readInt(); - return new Builder(type) - .setId(in.readString()) - .setPackageName(in.readString()) - .setTitleResId(in.readInt()) - .setTitleForWorkResId(in.readInt()) - .setSummaryResId(in.readInt()) - .setIntentAction(in.readString()) - .setProfile(in.readInt()) - .setInitialDisplayState(in.readInt()) - .setMaxSeverityLevel(in.readInt()) - .setSearchTermsResId(in.readInt()) - .setLoggingAllowed(in.readBoolean()) - .setRefreshOnPageOpenAllowed(in.readBoolean()) - .build(); + Builder builder = + new Builder(type) + .setId(in.readString()) + .setPackageName(in.readString()) + .setTitleResId(in.readInt()) + .setTitleForWorkResId(in.readInt()) + .setSummaryResId(in.readInt()) + .setIntentAction(in.readString()) + .setProfile(in.readInt()) + .setInitialDisplayState(in.readInt()) + .setMaxSeverityLevel(in.readInt()) + .setSearchTermsResId(in.readInt()) + .setLoggingAllowed(in.readBoolean()) + .setRefreshOnPageOpenAllowed(in.readBoolean()); + if (SdkLevel.isAtLeastU()) { + builder.setNotificationsAllowed(in.readBoolean()); + builder.setDeduplicationGroup(in.readString()); + List<String> certs = in.createStringArrayList(); + for (int i = 0; i < certs.size(); i++) { + builder.addPackageCertificateHash(certs.get(i)); + } + } + return builder.build(); } @Override @@ -187,6 +202,9 @@ public final class SafetySource implements Parcelable { @StringRes private final int mSearchTermsResId; private final boolean mLoggingAllowed; private final boolean mRefreshOnPageOpenAllowed; + private final boolean mNotificationsAllowed; + @Nullable final String mDeduplicationGroup; + @NonNull private final Set<String> mPackageCertificateHashes; private SafetySource( @SafetySourceType int type, @@ -201,7 +219,10 @@ public final class SafetySource implements Parcelable { int maxSeverityLevel, @StringRes int searchTermsResId, boolean loggingAllowed, - boolean refreshOnPageOpenAllowed) { + boolean refreshOnPageOpenAllowed, + boolean notificationsAllowed, + @Nullable String deduplicationGroup, + @NonNull Set<String> packageCertificateHashes) { mType = type; mId = id; mPackageName = packageName; @@ -215,6 +236,9 @@ public final class SafetySource implements Parcelable { mSearchTermsResId = searchTermsResId; mLoggingAllowed = loggingAllowed; mRefreshOnPageOpenAllowed = refreshOnPageOpenAllowed; + mNotificationsAllowed = notificationsAllowed; + mDeduplicationGroup = deduplicationGroup; + mPackageCertificateHashes = Set.copyOf(packageCertificateHashes); } /** Returns the type of this safety source. */ @@ -236,10 +260,14 @@ public final class SafetySource implements Parcelable { /** * Returns the package name of this safety source. * - * <p>This is the package that owns the source. The package will receive refresh requests and it - * can send set requests for the source. + * <p>This is the package that owns the source. The package will receive refresh requests, and + * it can send set requests for the source. The package is also used to create an explicit + * pending intent from the intent action in the package context. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type static. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_STATIC} even if the optional package name field for the + * source is set, for sources of type {@link SafetySource#SAFETY_SOURCE_TYPE_STATIC} use + * {@link SafetySource#getOptionalPackageName()} */ @NonNull public String getPackageName() { @@ -251,13 +279,36 @@ public final class SafetySource implements Parcelable { } /** + * Returns the package name of this safety source or null if undefined. + * + * <p>This is the package that owns the source. + * + * <p>The package is always defined for sources of type dynamic and issue-only. The package will + * receive refresh requests, and it can send set requests for sources of type dynamic and + * issue-only. The package is also used to create an explicit pending intent in the package + * context from the intent action if defined. + * + * <p>The package is optional for sources of type static. If present, the package is used to + * create an explicit pending intent in the package context from the intent action. + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public String getOptionalPackageName() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mPackageName; + } + + /** * Returns the resource id of the title of this safety source. * * <p>The id refers to a string resource that is either accessible from any resource context or * that is accessible from the same resource context that was used to load the Safety Center * configuration. The id is {@link Resources#ID_NULL} when a title is not provided. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} */ @StringRes public int getTitleResId() { @@ -275,8 +326,9 @@ public final class SafetySource implements Parcelable { * that is accessible from the same resource context that was used to load the Safety Center * configuration. The id is {@link Resources#ID_NULL} when a title for work is not provided. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only or if - * the profile property of the source is set to {@link SafetySource#PROFILE_PRIMARY}. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} or if the profile property of the source is + * set to {@link SafetySource#PROFILE_PRIMARY} */ @StringRes public int getTitleForWorkResId() { @@ -298,7 +350,8 @@ public final class SafetySource implements Parcelable { * that is accessible from the same resource context that was used to load the Safety Center * configuration. The id is {@link Resources#ID_NULL} when a summary is not provided. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} */ @StringRes public int getSummaryResId() { @@ -316,7 +369,8 @@ public final class SafetySource implements Parcelable { * source is displayed as an entry in the Safety Center page, and if the action is set to {@code * null} or if it does not resolve to an activity the source will be marked as disabled. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} */ @Nullable public String getIntentAction() { @@ -336,8 +390,9 @@ public final class SafetySource implements Parcelable { /** * Returns the initial display state of this safety source. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type static or - * issue-only. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_STATIC} or {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} */ @InitialDisplayState public int getInitialDisplayState() { @@ -361,7 +416,8 @@ public final class SafetySource implements Parcelable { * android.safetycenter.SafetySourceData#SEVERITY_LEVEL_INFORMATION} even if the maximum * severity level is set to a lower value. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type static. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_STATIC} */ public int getMaxSeverityLevel() { if (mType == SAFETY_SOURCE_TYPE_STATIC) { @@ -378,7 +434,8 @@ public final class SafetySource implements Parcelable { * that is accessible from the same resource context that was used to load the Safety Center * configuration. The id is {@link Resources#ID_NULL} when search terms are not provided. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} */ @StringRes public int getSearchTermsResId() { @@ -392,7 +449,8 @@ public final class SafetySource implements Parcelable { /** * Returns the logging allowed property of this safety source. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type static. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_STATIC} */ public boolean isLoggingAllowed() { if (mType == SAFETY_SOURCE_TYPE_STATIC) { @@ -408,7 +466,8 @@ public final class SafetySource implements Parcelable { * <p>If set to {@code true}, a refresh request will be sent to the source when the Safety * Center page is opened. * - * <p>Throws an {@link UnsupportedOperationException} if the source is of type static. + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_STATIC} */ public boolean isRefreshOnPageOpenAllowed() { if (mType == SAFETY_SOURCE_TYPE_STATIC) { @@ -418,6 +477,57 @@ public final class SafetySource implements Parcelable { return mRefreshOnPageOpenAllowed; } + /** + * Returns whether Safety Center may post Notifications about issues reported by this {@link + * SafetySource}. + * + * @see Builder#setNotificationsAllowed(boolean) + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public boolean areNotificationsAllowed() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mNotificationsAllowed; + } + + /** + * Returns the deduplication group this source belongs to. + * + * <p>Sources which are part of the same deduplication group can coordinate to deduplicate their + * issues. + */ + @Nullable + @RequiresApi(UPSIDE_DOWN_CAKE) + public String getDeduplicationGroup() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mDeduplicationGroup; + } + + /** + * Returns a set of package certificate hashes representing valid signed packages that represent + * this {@link SafetySource}. + * + * <p>If one or more certificate hashes are set, Safety Center will validate that a package + * calling {@link android.safetycenter.SafetyCenterManager#setSafetySourceData} is signed with + * one of the certificates provided. + * + * <p>The default value is an empty {@code Set}, in which case only the package name is + * validated. + * + * @see Builder#addPackageCertificateHash(String) + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Set<String> getPackageCertificateHashes() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + return mPackageCertificateHashes; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -435,7 +545,10 @@ public final class SafetySource implements Parcelable { && mMaxSeverityLevel == that.mMaxSeverityLevel && mSearchTermsResId == that.mSearchTermsResId && mLoggingAllowed == that.mLoggingAllowed - && mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed; + && mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed + && mNotificationsAllowed == that.mNotificationsAllowed + && Objects.equals(mDeduplicationGroup, that.mDeduplicationGroup) + && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes); } @Override @@ -453,7 +566,10 @@ public final class SafetySource implements Parcelable { mMaxSeverityLevel, mSearchTermsResId, mLoggingAllowed, - mRefreshOnPageOpenAllowed); + mRefreshOnPageOpenAllowed, + mNotificationsAllowed, + mDeduplicationGroup, + mPackageCertificateHashes); } @Override @@ -485,6 +601,12 @@ public final class SafetySource implements Parcelable { + mLoggingAllowed + ", mRefreshOnPageOpenAllowed=" + mRefreshOnPageOpenAllowed + + ", mNotificationsAllowed=" + + mNotificationsAllowed + + ", mDeduplicationGroup=" + + mDeduplicationGroup + + ", mPackageCertificateHashes=" + + mPackageCertificateHashes + '}'; } @@ -508,6 +630,11 @@ public final class SafetySource implements Parcelable { dest.writeInt(mSearchTermsResId); dest.writeBoolean(mLoggingAllowed); dest.writeBoolean(mRefreshOnPageOpenAllowed); + if (SdkLevel.isAtLeastU()) { + dest.writeBoolean(mNotificationsAllowed); + dest.writeString(mDeduplicationGroup); + dest.writeStringList(List.copyOf(mPackageCertificateHashes)); + } } /** Builder class for {@link SafetySource}. */ @@ -526,6 +653,9 @@ public final class SafetySource implements Parcelable { @Nullable @StringRes private Integer mSearchTermsResId; @Nullable private Boolean mLoggingAllowed; @Nullable private Boolean mRefreshOnPageOpenAllowed; + @Nullable private Boolean mNotificationsAllowed; + @Nullable private String mDeduplicationGroup; + @NonNull private final ArraySet<String> mPackageCertificateHashes = new ArraySet<>(); /** Creates a {@link Builder} for a {@link SafetySource}. */ public Builder(@SafetySourceType int type) { @@ -714,6 +844,61 @@ public final class SafetySource implements Parcelable { } /** + * Sets the {@link #areNotificationsAllowed()} property of this {@link SafetySource}. + * + * <p>If set to {@code true} Safety Center may post Notifications about issues reported by + * this source. + * + * <p>The default value is {@code false}. + * + * @see #areNotificationsAllowed() + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setNotificationsAllowed(boolean notificationsAllowed) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mNotificationsAllowed = notificationsAllowed; + return this; + } + + /** + * Sets the deduplication group for this source. + * + * <p>Sources which are part of the same deduplication group can coordinate to deduplicate + * issues that they're sending to SafetyCenter by providing the same deduplication + * identifier with those issues. + * + * <p>The deduplication group property is prohibited for sources of type static. + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setDeduplicationGroup(@Nullable String deduplicationGroup) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mDeduplicationGroup = deduplicationGroup; + return this; + } + + /** + * Adds a package certificate hash to the {@link #getPackageCertificateHashes()} property of + * this {@link SafetySource}. + * + * @see #getPackageCertificateHashes() + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder addPackageCertificateHash(@NonNull String packageCertificateHash) { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException(); + } + mPackageCertificateHashes.add(packageCertificateHash); + return this; + } + + /** * Creates the {@link SafetySource} defined by this {@link Builder}. * * <p>Throws an {@link IllegalStateException} if any constraint on the safety source is @@ -721,19 +906,25 @@ public final class SafetySource implements Parcelable { */ @NonNull public SafetySource build() { - if (mType != SAFETY_SOURCE_TYPE_STATIC - && mType != SAFETY_SOURCE_TYPE_DYNAMIC - && mType != SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + int type = mType; + if (type != SAFETY_SOURCE_TYPE_STATIC + && type != SAFETY_SOURCE_TYPE_DYNAMIC + && type != SAFETY_SOURCE_TYPE_ISSUE_ONLY) { throw new IllegalStateException("Unexpected type"); } - boolean isStatic = mType == SAFETY_SOURCE_TYPE_STATIC; - boolean isDynamic = mType == SAFETY_SOURCE_TYPE_DYNAMIC; - boolean isIssueOnly = mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY; + boolean isStatic = type == SAFETY_SOURCE_TYPE_STATIC; + boolean isDynamic = type == SAFETY_SOURCE_TYPE_DYNAMIC; + boolean isIssueOnly = type == SAFETY_SOURCE_TYPE_ISSUE_ONLY; - BuilderUtils.validateAttribute(mId, "id", true, false); + String id = mId; + BuilderUtils.validateAttribute(id, "id", true, false); + String packageName = mPackageName; BuilderUtils.validateAttribute( - mPackageName, "packageName", isDynamic || isIssueOnly, isStatic); + packageName, + "packageName", + isDynamic || isIssueOnly, + isStatic && !SdkLevel.isAtLeastU()); int initialDisplayState = BuilderUtils.validateIntDef( @@ -781,8 +972,9 @@ public final class SafetySource implements Parcelable { BuilderUtils.validateResId( mSummaryResId, "summary", isDynamicNotHidden, isIssueOnly); + String intentAction = mIntentAction; BuilderUtils.validateAttribute( - mIntentAction, + intentAction, "intentAction", (isDynamic && isEnabled) || isStatic, isIssueOnly); @@ -807,20 +999,41 @@ public final class SafetySource implements Parcelable { isStatic, false); + String deduplicationGroup = mDeduplicationGroup; + boolean notificationsAllowed = false; + Set<String> packageCertificateHashes = Set.copyOf(mPackageCertificateHashes); + if (SdkLevel.isAtLeastU()) { + notificationsAllowed = + BuilderUtils.validateBoolean( + mNotificationsAllowed, + "notificationsAllowed", + false, + isStatic, + false); + + BuilderUtils.validateAttribute( + deduplicationGroup, "deduplicationGroup", false, isStatic); + BuilderUtils.validateCollection( + packageCertificateHashes, "packageCertificateHashes", false, isStatic); + } + return new SafetySource( - mType, - mId, - mPackageName, + type, + id, + packageName, titleResId, titleForWorkResId, summaryResId, - mIntentAction, + intentAction, profile, initialDisplayState, maxSeverityLevel, searchTermsResId, loggingAllowed, - refreshOnPageOpenAllowed); + refreshOnPageOpenAllowed, + notificationsAllowed, + deduplicationGroup, + packageCertificateHashes); } } } diff --git a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java index 2868ee7ec..c3e5a4720 100644 --- a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java +++ b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java @@ -17,6 +17,7 @@ package android.safetycenter.config; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; @@ -25,6 +26,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.res.Resources; import android.os.Parcel; @@ -32,6 +34,8 @@ import android.os.Parcelable; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -50,16 +54,39 @@ public final class SafetySourcesGroup implements Parcelable { /** * Indicates that the safety sources group should be displayed as a collapsible group with an * icon (stateless or stateful) and an optional default summary. + * + * @deprecated use {@link #SAFETY_SOURCES_GROUP_TYPE_STATEFUL} instead. */ public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0; /** + * Indicates that the safety sources group should be displayed as a group that may contribute to + * the overall Safety Center status. This is indicated by a group stateful icon. If all sources + * in the group have an unspecified status then a stateless group icon might be applied. + */ + public static final int SAFETY_SOURCES_GROUP_TYPE_STATEFUL = + SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; + + /** * Indicates that the safety sources group should be displayed as a rigid group with no icon and * no summary. + * + * @deprecated use {@link #SAFETY_SOURCES_GROUP_TYPE_STATELESS} instead. */ public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1; - /** Indicates that the safety sources group should not be displayed. */ + /** + * Indicates that the safety sources group should be displayed as a group that does not + * contribute to the overall Safety Center status. All sources of type dynamic in the group can + * only report an unspecified status. The stateless icon and summary may be ignored and not be + * displayed. + */ + public static final int SAFETY_SOURCES_GROUP_TYPE_STATELESS = SAFETY_SOURCES_GROUP_TYPE_RIGID; + + /** + * Indicates that the safety sources group should not be displayed. All sources in the group + * must be of type issue-only. + */ public static final int SAFETY_SOURCES_GROUP_TYPE_HIDDEN = 2; /** @@ -67,24 +94,27 @@ public final class SafetySourcesGroup implements Parcelable { * * @hide */ + @SuppressLint("UniqueConstants") // Intentionally renaming the COLLAPSIBLE and RIGID constants. @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = "SAFETY_SOURCES_GROUP_TYPE_", value = { SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE, + SAFETY_SOURCES_GROUP_TYPE_STATEFUL, SAFETY_SOURCES_GROUP_TYPE_RIGID, + SAFETY_SOURCES_GROUP_TYPE_STATELESS, SAFETY_SOURCES_GROUP_TYPE_HIDDEN }) public @interface SafetySourceGroupType {} /** - * Indicates that the safety sources group will not be displayed with any special icon when all - * the sources contained in it are stateless. + * Indicates that no special icon will be displayed by a safety sources group when all the + * sources contained in it are stateless. */ public static final int STATELESS_ICON_TYPE_NONE = 0; /** - * Indicates that the safety sources group will be displayed with the privacy icon when all the + * Indicates that the privacy icon will be displayed by a safety sources group when all the * sources contained in it are stateless. */ public static final int STATELESS_ICON_TYPE_PRIVACY = 1; @@ -116,6 +146,9 @@ public final class SafetySourcesGroup implements Parcelable { for (int i = 0; i < safetySources.size(); i++) { builder.addSafetySource(safetySources.get(i)); } + if (SdkLevel.isAtLeastU()) { + builder.setType(in.readInt()); + } return builder.build(); } @@ -125,6 +158,7 @@ public final class SafetySourcesGroup implements Parcelable { } }; + @SafetySourceGroupType private final int mType; @NonNull private final String mId; @StringRes private final int mTitleResId; @StringRes private final int mSummaryResId; @@ -132,11 +166,13 @@ public final class SafetySourcesGroup implements Parcelable { @NonNull private final List<SafetySource> mSafetySources; private SafetySourcesGroup( + @SafetySourceGroupType int type, @NonNull String id, @StringRes int titleResId, @StringRes int summaryResId, @StatelessIconType int statelessIconType, @NonNull List<SafetySource> safetySources) { + mType = type; mId = id; mTitleResId = titleResId; mSummaryResId = summaryResId; @@ -144,23 +180,10 @@ public final class SafetySourcesGroup implements Parcelable { mSafetySources = safetySources; } - /** - * Returns the type of this safety sources group. - * - * <p>The type is inferred according to the state of certain fields. If no title is provided - * when building the group, the group is of type hidden. If a title is provided but no summary - * or stateless icon are provided when building the group, the group is of type rigid. - * Otherwise, the group is of type collapsible. - */ + /** Returns the type of this safety sources group. */ @SafetySourceGroupType public int getType() { - if (mTitleResId == Resources.ID_NULL) { - return SAFETY_SOURCES_GROUP_TYPE_HIDDEN; - } - if (mSummaryResId != Resources.ID_NULL || mStatelessIconType != STATELESS_ICON_TYPE_NONE) { - return SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; - } - return SAFETY_SOURCES_GROUP_TYPE_RIGID; + return mType; } /** @@ -224,7 +247,8 @@ public final class SafetySourcesGroup implements Parcelable { if (this == o) return true; if (!(o instanceof SafetySourcesGroup)) return false; SafetySourcesGroup that = (SafetySourcesGroup) o; - return Objects.equals(mId, that.mId) + return mType == that.mType + && Objects.equals(mId, that.mId) && mTitleResId == that.mTitleResId && mSummaryResId == that.mSummaryResId && mStatelessIconType == that.mStatelessIconType @@ -233,13 +257,16 @@ public final class SafetySourcesGroup implements Parcelable { @Override public int hashCode() { - return Objects.hash(mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources); + return Objects.hash( + mType, mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources); } @Override public String toString() { return "SafetySourcesGroup{" - + "mId=" + + "mType=" + + mType + + ", mId=" + mId + ", mTitleResId=" + mTitleResId @@ -264,6 +291,9 @@ public final class SafetySourcesGroup implements Parcelable { dest.writeInt(mSummaryResId); dest.writeInt(mStatelessIconType); dest.writeTypedList(mSafetySources); + if (SdkLevel.isAtLeastU()) { + dest.writeInt(mType); + } } /** Builder class for {@link SafetySourcesGroup}. */ @@ -271,6 +301,7 @@ public final class SafetySourcesGroup implements Parcelable { private final List<SafetySource> mSafetySources = new ArrayList<>(); + @Nullable @SafetySourceGroupType private Integer mType; @Nullable private String mId; @Nullable @StringRes private Integer mTitleResId; @Nullable @StringRes private Integer mSummaryResId; @@ -280,6 +311,22 @@ public final class SafetySourcesGroup implements Parcelable { public Builder() {} /** + * Sets the type of this safety sources group. + * + * <p>If the type is not explicitly set, the type is inferred according to the state of + * certain fields. If no title is provided when building the group, the group is of type + * hidden. If a title is provided but no summary or stateless icon are provided when + * building the group, the group is of type stateless. Otherwise, the group is of type + * stateful. + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setType(@SafetySourceGroupType int type) { + mType = type; + return this; + } + + /** * Sets the id of this safety sources group. * * <p>The id must be unique among safety sources groups in a Safety Center configuration. @@ -346,27 +393,20 @@ public final class SafetySourcesGroup implements Parcelable { /** * Creates the {@link SafetySourcesGroup} defined by this {@link Builder}. * - * <p>Throws an {@link IllegalStateException} if any constraint on the safety sources group - * is violated. + * @throws IllegalStateException if any constraint on the safety sources group is violated */ @NonNull public SafetySourcesGroup build() { - BuilderUtils.validateAttribute(mId, "id", true, false); + String id = mId; + BuilderUtils.validateAttribute(id, "id", true, false); + List<SafetySource> safetySources = unmodifiableList(new ArrayList<>(mSafetySources)); if (safetySources.isEmpty()) { throw new IllegalStateException("Safety sources group empty"); } - boolean titleRequired = false; - int safetySourcesSize = safetySources.size(); - for (int i = 0; i < safetySourcesSize; i++) { - int type = safetySources.get(i).getType(); - if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) { - titleRequired = true; - break; - } - } - int titleResId = BuilderUtils.validateResId(mTitleResId, "title", titleRequired, false); + int summaryResId = BuilderUtils.validateResId(mSummaryResId, "summary", false, false); + int statelessIconType = BuilderUtils.validateIntDef( mStatelessIconType, @@ -376,8 +416,53 @@ public final class SafetySourcesGroup implements Parcelable { STATELESS_ICON_TYPE_NONE, STATELESS_ICON_TYPE_NONE, STATELESS_ICON_TYPE_PRIVACY); + + boolean hasOnlyIssueOnlySources = true; + int safetySourcesSize = safetySources.size(); + for (int i = 0; i < safetySourcesSize; i++) { + int type = safetySources.get(i).getType(); + if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + hasOnlyIssueOnlySources = false; + break; + } + } + + int inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_STATELESS; + if (hasOnlyIssueOnlySources) { + inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_HIDDEN; + } else if (summaryResId != Resources.ID_NULL + || statelessIconType != Resources.ID_NULL) { + inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_STATEFUL; + } + int type = + BuilderUtils.validateIntDef( + mType, + "type", + false, + false, + inferredGroupType, + SAFETY_SOURCES_GROUP_TYPE_STATEFUL, + SAFETY_SOURCES_GROUP_TYPE_STATELESS, + SAFETY_SOURCES_GROUP_TYPE_HIDDEN); + if (type == SAFETY_SOURCES_GROUP_TYPE_HIDDEN && !hasOnlyIssueOnlySources) { + throw new IllegalStateException( + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only"); + } + if (type != SAFETY_SOURCES_GROUP_TYPE_HIDDEN && hasOnlyIssueOnlySources) { + throw new IllegalStateException( + "Safety sources groups containing only sources of type issue-only must be " + + "of type hidden"); + } + + boolean isStateful = type == SAFETY_SOURCES_GROUP_TYPE_STATEFUL; + boolean isStateless = type == SAFETY_SOURCES_GROUP_TYPE_STATELESS; + int titleResId = + BuilderUtils.validateResId( + mTitleResId, "title", isStateful || isStateless, false); + return new SafetySourcesGroup( - mId, titleResId, summaryResId, statelessIconType, safetySources); + type, id, titleResId, summaryResId, statelessIconType, safetySources); } } } diff --git a/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd new file mode 100644 index 000000000..3a70fa065 --- /dev/null +++ b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd @@ -0,0 +1,204 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!-- This file contains comments that define constraints that cannot be covered by the XSD language --> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + version="1.0"> + + <xsd:element name="safety-center-config" type="safety-center-config"/> + + <xsd:complexType name="safety-center-config"> + <xsd:sequence> + <xsd:element name="safety-sources-config" type="safety-sources-config"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="safety-sources-config"> + <xsd:sequence> + <xsd:element + name="safety-sources-group" type="safety-sources-group" + minOccurs="1" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="safety-sources-group"> + <xsd:choice minOccurs="1" maxOccurs="unbounded"> + <xsd:element name="dynamic-safety-source" type="dynamic-safety-source"/> + <xsd:element name="static-safety-source" type="static-safety-source"/> + <xsd:element name="issue-only-safety-source" type="issue-only-safety-source"/> + </xsd:choice> + <!-- id must be unique among safety sources groups --> + <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/> + <!-- title is required unless the group contains issue only and/or internal sources --> + <xsd:attribute name="title" type="runtimeStringResourceName"/> + <xsd:attribute name="summary" type="runtimeStringResourceName"/> + <xsd:attribute name="statelessIconType" type="statelessIconTypeOrStringResourceName" + default="none"/> + <!-- type is inferred from other attributes and the group content if omitted --> + <xsd:attribute name="type" type="groupTypeOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="dynamic-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/> + <!-- optional comma-separated set of certficate hashes, if provided will be used for validation. --> + <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/> + <!-- title is required if initialDisplayState is not set to hidden or if searchTerms are provided --> + <xsd:attribute name="title" type="runtimeStringResourceName"/> + <!-- titleForWork is required if profile is set to all_profiles, and initialDisplayState is not set to hidden or if searchTerms are provided --> + <!-- titleForWork is prohibited if profile is set to primary_profile_only --> + <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/> + <!-- summary is required if initialDisplayState is not set to hidden --> + <xsd:attribute name="summary" type="runtimeStringResourceName"/> + <!-- intentAction is required if initialDisplayState is set to enabled --> + <xsd:attribute name="intentAction" type="stringOrStringResourceName"/> + <xsd:attribute name="profile" type="profile" use="required"/> + <xsd:attribute name="initialDisplayState" type="initialDisplayStateOrStringResourceName" + default="enabled"/> + <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/> + <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/> + <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/> + <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="notificationsAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="issue-only-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/> + <!-- optional comma-separated set of certficate hashes, if provided will be used for validation. --> + <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/> + <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/> + <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/> + <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/> + <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="notificationsAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="static-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName"/> + <xsd:attribute name="title" type="runtimeStringResourceName" use="required"/> + <!-- titleForWork is required if profile is set to all_profiles --> + <!-- titleForWork is prohibited if profile is set to primary_profile_only --> + <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/> + <xsd:attribute name="summary" type="runtimeStringResourceName"/> + <xsd:attribute name="intentAction" type="stringOrStringResourceName" use="required"/> + <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/> + <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/> + </xsd:complexType> + + <xsd:simpleType name="intOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type xsd:int. --> + <xsd:union memberTypes="stringResourceName xsd:int"/> + </xsd:simpleType> + + <xsd:simpleType name="booleanOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type xsd:boolean. --> + <xsd:union memberTypes="stringResourceName xsd:boolean"/> + </xsd:simpleType> + + <xsd:simpleType name="stringOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type xsd:string. --> + <xsd:union memberTypes="stringResourceName xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="statelessIconTypeOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type statelessIconType. --> + <xsd:union memberTypes="stringResourceName statelessIconType"/> + </xsd:simpleType> + + <xsd:simpleType name="statelessIconType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="none"/> + <xsd:enumeration value="privacy"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="profileOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type profile. --> + <xsd:union memberTypes="stringResourceName profile"/> + </xsd:simpleType> + + <xsd:simpleType name="profile"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="primary_profile_only"/> + <xsd:enumeration value="all_profiles"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="initialDisplayStateOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type initialDisplayState. --> + <xsd:union memberTypes="stringResourceName initialDisplayState"/> + </xsd:simpleType> + + <xsd:simpleType name="initialDisplayState"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="enabled"/> + <xsd:enumeration value="disabled"/> + <xsd:enumeration value="hidden"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="groupTypeOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type groupType. --> + <xsd:union memberTypes="stringResourceName groupType"/> + </xsd:simpleType> + + <xsd:simpleType name="groupType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="stateless"/> + <xsd:enumeration value="stateful"/> + <xsd:enumeration value="hidden"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="runtimeStringResourceName"> + <!-- String resource names will be resolved at runtime whenever the string value is used. --> + <xsd:union memberTypes="stringResourceName"/> + </xsd:simpleType> + + <!-- String resource names will be ignored for any attribute not directly or indirectly marked as stringResourceName. --> + <!-- A stringResourceName is a fully qualified resource name of the form "@package:string/entry". Package is required. --> + <xsd:simpleType name="stringResourceName"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="@([a-z]+\.)*[a-z]+:string/.+"/> + </xsd:restriction> + </xsd:simpleType> + +</xsd:schema> diff --git a/service/Android.bp b/service/Android.bp index 9846e7e31..4fa96f235 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -20,30 +20,23 @@ filegroup { name: "service-permission-java-sources", srcs: [ "java/**/*.java", - // Exclude Kotlin sources for T. - //"java/**/*.kt", + "java/**/*.kt", ], path: "java", visibility: ["//visibility:private"], } -filegroup { - name: "service-permission-streaming-proto-sources", - srcs: [ - "proto/role_service.proto", - ], - visibility: ["//frameworks/base"], -} - java_library { - name: "service-permission-streaming-proto-java-gen", + name: "service-permission-proto-stream", proto: { type: "stream", include_dirs: [ "external/protobuf/src", ], }, - srcs: [":service-permission-streaming-proto-sources"], + srcs: [ + "proto/role_service.proto", + ], installable: false, min_sdk_version: "30", sdk_version: "system_server_current", @@ -78,7 +71,7 @@ java_sdk_library { impl_library_visibility: [ "//frameworks/base/apex/permission/tests", "//frameworks/base/services/tests/mockingservicestests", - "//frameworks/base/services/tests/servicestests", + "//frameworks/base/services/tests/PackageManagerServiceTests/server", "//packages/modules/Permission/tests/apex", ], srcs: [ @@ -86,6 +79,7 @@ java_sdk_library { ], libs: [ "androidx.annotation_annotation", + "framework-configinfrastructure", // TODO(b/177884622): Short term solution to prevent service-permission from seeing hidden // APIs in framework-permission, as we don't actually have any dependency in it. //"framework-permission", @@ -96,13 +90,12 @@ java_sdk_library { // Soong fails to automatically add this dependency because all the // *.kt sources are inside a filegroup. - // Exclude Kotlin sources for T. - //"kotlin-annotations", + "kotlin-annotations", ], static_libs: [ - // Exclude Kotlin sources for T. - //"kotlin-stdlib", + "kotlin-stdlib", "modules-utils-backgroundthread", + "modules-utils-binary-xml", "modules-utils-os", "safety-center-config", "safety-center-internal-data", @@ -110,7 +103,7 @@ java_sdk_library { "safety-center-resources-lib", "service-permission-shared", "service-permission-statsd", - "service-permission-streaming-proto-java-gen", + "service-permission-proto-stream", ], errorprone: { javacflags: ["-Xep:GuardedBy:ERROR"], diff --git a/service/java/com/android/permission/compat/UserHandleCompat.java b/service/java/com/android/permission/compat/UserHandleCompat.java index 7c711d301..1901aa997 100644 --- a/service/java/com/android/permission/compat/UserHandleCompat.java +++ b/service/java/com/android/permission/compat/UserHandleCompat.java @@ -45,4 +45,15 @@ public final class UserHandleCompat { public static int getUserId(int uid) { return UserHandle.getUserHandleForUid(uid).getIdentifier(); } + + /** + * Get the UID from the give user ID and app ID + * + * @param userId the user ID + * @param appId the app ID + * @return the UID + */ + public static int getUid(@UserIdInt int userId, int appId) { + return UserHandle.of(userId).getUid(appId); + } } diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java index bb787b7cf..bc8d58a8a 100644 --- a/service/java/com/android/role/RoleService.java +++ b/service/java/com/android/role/RoleService.java @@ -272,15 +272,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback RoleControllerManager controller = mControllers.get(userId); if (controller == null) { Context systemContext = getContext(); - Context context; - try { - context = systemContext.createPackageContextAsUser( - systemContext.getPackageName(), 0, UserHandle.of(userId)); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } + Context userContext = systemContext.createContextAsUser(UserHandle.of(userId), 0); controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName( - ForegroundThread.getHandler(), context); + ForegroundThread.getHandler(), userContext); mControllers.put(userId, controller); } return controller; diff --git a/service/java/com/android/role/RoleShellCommand.java b/service/java/com/android/role/RoleShellCommand.java index 357ce5f76..808a64cb4 100644 --- a/service/java/com/android/role/RoleShellCommand.java +++ b/service/java/com/android/role/RoleShellCommand.java @@ -29,11 +29,14 @@ import com.android.modules.utils.BasicShellCommandHandler; import com.android.permission.compat.UserHandleCompat; import java.io.PrintWriter; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @RequiresApi(Build.VERSION_CODES.S) class RoleShellCommand extends BasicShellCommandHandler { + private static final String ROLE_HOLDER_SEPARATOR = ";"; + @NonNull private final IRoleManager mRoleManager; @@ -74,6 +77,8 @@ class RoleShellCommand extends BasicShellCommandHandler { PrintWriter pw = getOutPrintWriter(); try { switch (cmd) { + case "get-role-holders": + return runGetRoleHolders(); case "add-role-holder": return runAddRoleHolder(); case "remove-role-holder": @@ -108,6 +113,15 @@ class RoleShellCommand extends BasicShellCommandHandler { return Integer.parseInt(flags); } + private int runGetRoleHolders() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + + List<String> roleHolders = mRoleManager.getRoleHoldersAsUser(roleName, userId); + getOutPrintWriter().println(String.join(ROLE_HOLDER_SEPARATOR, roleHolders)); + return 0; + } + private int runAddRoleHolder() throws RemoteException { int userId = getUserIdMaybe(); String roleName = getNextArgRequired(); @@ -155,6 +169,7 @@ class RoleShellCommand extends BasicShellCommandHandler { pw.println(" help or -h"); pw.println(" Print this help text."); pw.println(); + pw.println(" get-role-holders [--user USER_ID] ROLE"); pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]"); diff --git a/service/java/com/android/role/TEST_MAPPING b/service/java/com/android/role/TEST_MAPPING index 0d7bc1476..15173a9da 100644 --- a/service/java/com/android/role/TEST_MAPPING +++ b/service/java/com/android/role/TEST_MAPPING @@ -1,10 +1,10 @@ { "presubmit": [ { - "name": "CtsStatsdHostTestCases", + "name": "CtsAppSecurityHostTestCases", "options": [ { - "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder" + "include-filter": "android.appsecurity.cts.StatsdAppSecurityAtomTest#testRoleHolder" } ] }, @@ -16,5 +16,22 @@ } ] } + ], + "mainline-presubmit": [ + { + "name": "CtsRoleTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677748): These two tests currently fails on R base image + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList" + }, + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } ] } diff --git a/service/java/com/android/safetycenter/PendingIntentFactory.java b/service/java/com/android/safetycenter/PendingIntentFactory.java index 631f6fc92..42dcbcabd 100644 --- a/service/java/com/android/safetycenter/PendingIntentFactory.java +++ b/service/java/com/android/safetycenter/PendingIntentFactory.java @@ -26,6 +26,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.res.Resources; import android.os.Binder; import android.os.UserHandle; @@ -34,7 +35,6 @@ import android.util.Log; import androidx.annotation.RequiresApi; -import com.android.permission.util.PackageUtils; import com.android.safetycenter.resources.SafetyCenterResourcesContext; import java.util.Arrays; @@ -92,8 +92,7 @@ final class PendingIntentFactory { if (packageContext == null) { return null; } - Intent intent = - createIntent(sourceId, intentAction, packageName, userId, isQuietModeEnabled); + Intent intent = createIntent(packageContext, sourceId, intentAction, isQuietModeEnabled); if (intent == null) { return null; } @@ -208,10 +207,9 @@ final class PendingIntentFactory { @Nullable private Intent createIntent( + @NonNull Context packageContext, @NonNull String sourceId, @NonNull String intentAction, - @NonNull String packageName, - @UserIdInt int userId, boolean isQuietModeEnabled) { Intent intent = new Intent(intentAction); @@ -231,11 +229,11 @@ final class PendingIntentFactory { if (isQuietModeEnabled) { return intent; } - if (intentResolves(intent, userId)) { + if (intentResolves(packageContext, intent)) { return intent; } - intent.setPackage(packageName); - if (intentResolves(intent, userId)) { + intent.setPackage(packageContext.getPackageName()); + if (intentResolves(packageContext, intent)) { return intent; } return null; @@ -249,8 +247,10 @@ final class PendingIntentFactory { .contains(sourceId); } - private boolean intentResolves(@NonNull Intent intent, @UserIdInt int userId) { - return !PackageUtils.queryUnfilteredIntentActivitiesAsUser(intent, 0, userId, mContext) + private static boolean intentResolves(@NonNull Context packageContext, @NonNull Intent intent) { + return !packageContext + .getPackageManager() + .queryIntentActivities(intent, ResolveInfoFlags.of(0)) .isEmpty(); } diff --git a/service/java/com/android/safetycenter/RefreshReasons.java b/service/java/com/android/safetycenter/RefreshReasons.java index c5f860a7a..ee318c7fd 100644 --- a/service/java/com/android/safetycenter/RefreshReasons.java +++ b/service/java/com/android/safetycenter/RefreshReasons.java @@ -17,21 +17,26 @@ package com.android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA; import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHANGE; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN; +import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED; +import android.annotation.TargetApi; import android.safetycenter.SafetyCenterManager.RefreshReason; import android.safetycenter.SafetyCenterManager.RefreshRequestType; import android.util.Log; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + /** Helpers to do with {@link RefreshReason}. */ @RequiresApi(TIRAMISU) final class RefreshReasons { @@ -54,10 +59,14 @@ final class RefreshReasons { case REFRESH_REASON_OTHER: return; } + if (SdkLevel.isAtLeastU() && refreshReason == REFRESH_REASON_PERIODIC) { + return; + } throw new IllegalArgumentException("Unexpected refresh reason: " + refreshReason); } /** Converts the given {@link RefreshReason} to a {@link RefreshRequestType}. */ + @TargetApi(UPSIDE_DOWN_CAKE) @RefreshRequestType static int toRefreshRequestType(@RefreshReason int refreshReason) { switch (refreshReason) { @@ -68,6 +77,7 @@ final class RefreshReasons { case REFRESH_REASON_DEVICE_LOCALE_CHANGE: case REFRESH_REASON_SAFETY_CENTER_ENABLED: case REFRESH_REASON_OTHER: + case REFRESH_REASON_PERIODIC: return EXTRA_REFRESH_REQUEST_TYPE_GET_DATA; } Log.w(TAG, "Unexpected refresh reason: " + refreshReason); @@ -77,12 +87,14 @@ final class RefreshReasons { /** * Returns {@code true} if the given {@link RefreshReason} corresponds to a background refresh. */ + @TargetApi(UPSIDE_DOWN_CAKE) static boolean isBackgroundRefresh(@RefreshReason int refreshReason) { switch (refreshReason) { case REFRESH_REASON_DEVICE_REBOOT: case REFRESH_REASON_DEVICE_LOCALE_CHANGE: case REFRESH_REASON_SAFETY_CENTER_ENABLED: case REFRESH_REASON_OTHER: + case REFRESH_REASON_PERIODIC: return true; case REFRESH_REASON_PAGE_OPEN: case REFRESH_REASON_RESCAN_BUTTON_CLICK: diff --git a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java index 4deb642f7..65f6bdfd5 100644 --- a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java +++ b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java @@ -83,10 +83,15 @@ final class SafetyCenterBroadcastDispatcher { * SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES}, and returns the associated broadcast id. * * <p>Returns {@code null} if no broadcast was sent. + * + * @param safetySourceIds list of IDs to specify the safety sources to be refreshed or a {@code + * null} value to refresh all safety sources. */ @Nullable String sendRefreshSafetySources( - @RefreshReason int refreshReason, @NonNull UserProfileGroup userProfileGroup) { + @RefreshReason int refreshReason, + @NonNull UserProfileGroup userProfileGroup, + @Nullable List<String> safetySourceIds) { List<Broadcast> broadcasts = mSafetyCenterConfigReader.getBroadcasts(); BroadcastOptions broadcastOptions = createBroadcastOptions(); @@ -104,7 +109,8 @@ final class SafetyCenterBroadcastDispatcher { broadcastOptions, refreshReason, userProfileGroup, - broadcastId); + broadcastId, + safetySourceIds); } if (!hasSentAtLeastOneBroadcast) { @@ -120,7 +126,8 @@ final class SafetyCenterBroadcastDispatcher { @NonNull BroadcastOptions broadcastOptions, @RefreshReason int refreshReason, @NonNull UserProfileGroup userProfileGroup, - @NonNull String broadcastId) { + @NonNull String broadcastId, + @Nullable List<String> requiredSourceIds) { boolean hasSentAtLeastOneBroadcast = false; int requestType = RefreshReasons.toRefreshRequestType(refreshReason); String packageName = broadcast.getPackageName(); @@ -137,6 +144,11 @@ final class SafetyCenterBroadcastDispatcher { sourceIds.removeAll(deniedSourceIds); } + if (requiredSourceIds != null) { + sourceIds = new ArrayList<>(sourceIds); + sourceIds.retainAll(requiredSourceIds); + } + if (sourceIds.isEmpty()) { continue; } diff --git a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java index d09807abb..38d4d998c 100644 --- a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java +++ b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java @@ -315,14 +315,15 @@ final class SafetyCenterConfigReader { continue; } - boolean hasEntryInRigidGroup = + boolean hasEntryInStatelessGroup = safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC && safetySourcesGroup.getType() - == SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID; + == SafetySourcesGroup + .SAFETY_SOURCES_GROUP_TYPE_STATELESS; externalSafetySources.put( safetySource.getId(), - new ExternalSafetySource(safetySource, hasEntryInRigidGroup)); + new ExternalSafetySource(safetySource, hasEntryInStatelessGroup)); } } @@ -383,14 +384,14 @@ final class SafetyCenterConfigReader { broadcasts.add(broadcast); } broadcast.mSourceIdsForProfileParent.add(safetySource.getId()); - if (safetySource.isRefreshOnPageOpenAllowed()) { + if (isRefreshOnPageOpenAllowedAfterApplyingOverrides(safetySource)) { broadcast.mSourceIdsForProfileParentOnPageOpen.add(safetySource.getId()); } boolean needsManagedProfilesBroadcast = SafetySources.supportsManagedProfiles(safetySource); if (needsManagedProfilesBroadcast) { broadcast.mSourceIdsForManagedProfiles.add(safetySource.getId()); - if (safetySource.isRefreshOnPageOpenAllowed()) { + if (isRefreshOnPageOpenAllowedAfterApplyingOverrides(safetySource)) { broadcast.mSourceIdsForManagedProfilesOnPageOpen.add( safetySource.getId()); } @@ -402,15 +403,22 @@ final class SafetyCenterConfigReader { } } + private static boolean isRefreshOnPageOpenAllowedAfterApplyingOverrides( + SafetySource safetySource) { + return safetySource.isRefreshOnPageOpenAllowed() + || SafetyCenterFlags.getOverrideRefreshOnPageOpenSourceIds() + .contains(safetySource.getId()); + } + /** A wrapper class around a {@link SafetySource} that is providing data externally. */ static final class ExternalSafetySource { @NonNull private final SafetySource mSafetySource; - @NonNull private final boolean mHasEntryInRigidGroup; + @NonNull private final boolean mHasEntryInStatelessGroup; private ExternalSafetySource( - @NonNull SafetySource safetySource, boolean hasEntryInRigidGroup) { + @NonNull SafetySource safetySource, boolean hasEntryInStatelessGroup) { mSafetySource = safetySource; - mHasEntryInRigidGroup = hasEntryInRigidGroup; + mHasEntryInStatelessGroup = hasEntryInStatelessGroup; } /** Returns the external {@link SafetySource}. */ @@ -420,11 +428,11 @@ final class SafetyCenterConfigReader { } /** - * Returns whether the external {@link SafetySource} has an entry in a rigid {@link + * Returns whether the external {@link SafetySource} has an entry in a stateless {@link * SafetySourcesGroup}. */ - boolean hasEntryInRigidGroup() { - return mHasEntryInRigidGroup; + boolean hasEntryInStatelessGroup() { + return mHasEntryInStatelessGroup; } @Override @@ -432,13 +440,13 @@ final class SafetyCenterConfigReader { if (this == o) return true; if (!(o instanceof ExternalSafetySource)) return false; ExternalSafetySource that = (ExternalSafetySource) o; - return mHasEntryInRigidGroup == that.mHasEntryInRigidGroup + return mHasEntryInStatelessGroup == that.mHasEntryInStatelessGroup && mSafetySource.equals(that.mSafetySource); } @Override public int hashCode() { - return Objects.hash(mSafetySource, mHasEntryInRigidGroup); + return Objects.hash(mSafetySource, mHasEntryInStatelessGroup); } @Override @@ -446,8 +454,8 @@ final class SafetyCenterConfigReader { return "ExternalSafetySource{" + "mSafetySource=" + mSafetySource - + ", mHasEntryInRigidGroup=" - + mHasEntryInRigidGroup + + ", mHasEntryInStatelessGroup=" + + mHasEntryInStatelessGroup + '}'; } } diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java index efc50b318..08f3e226f 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java @@ -48,6 +48,7 @@ import android.util.Log; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; import com.android.safetycenter.internaldata.SafetyCenterEntryId; import com.android.safetycenter.internaldata.SafetyCenterIds; import com.android.safetycenter.internaldata.SafetyCenterIssueActionId; @@ -87,19 +88,24 @@ final class SafetyCenterDataFactory { @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache; @NonNull private final SafetyCenterRepository mSafetyCenterRepository; + /** Only available on Android U+. */ + @Nullable private final SafetyCenterIssueDeduplicator mSafetyCenterIssueDeduplicator; + SafetyCenterDataFactory( @NonNull SafetyCenterResourcesContext safetyCenterResourcesContext, @NonNull SafetyCenterConfigReader safetyCenterConfigReader, @NonNull SafetyCenterRefreshTracker safetyCenterRefreshTracker, @NonNull PendingIntentFactory pendingIntentFactory, @NonNull SafetyCenterIssueCache safetyCenterIssueCache, - @NonNull SafetyCenterRepository safetyCenterRepository) { + @NonNull SafetyCenterRepository safetyCenterRepository, + @Nullable SafetyCenterIssueDeduplicator safetyCenterIssueDeduplicator) { mSafetyCenterResourcesContext = safetyCenterResourcesContext; mSafetyCenterConfigReader = safetyCenterConfigReader; mSafetyCenterRefreshTracker = safetyCenterRefreshTracker; mPendingIntentFactory = pendingIntentFactory; mSafetyCenterIssueCache = safetyCenterIssueCache; mSafetyCenterRepository = safetyCenterRepository; + mSafetyCenterIssueDeduplicator = safetyCenterIssueDeduplicator; } /** @@ -107,13 +113,17 @@ final class SafetyCenterDataFactory { */ @NonNull static SafetyCenterData getDefaultSafetyCenterData() { - return new SafetyCenterData( + SafetyCenterStatus defaultSafetyCenterStatus = new SafetyCenterStatus.Builder("", "") .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN) - .build(), - emptyList(), - emptyList(), - emptyList()); + .build(); + if (SdkLevel.isAtLeastU()) { + return new SafetyCenterData( + defaultSafetyCenterStatus, emptyList(), emptyList(), emptyList(), emptyList()); + } else { + return new SafetyCenterData( + defaultSafetyCenterStatus, emptyList(), emptyList(), emptyList()); + } } /** @@ -142,7 +152,7 @@ final class SafetyCenterDataFactory { @NonNull String packageName, @NonNull UserProfileGroup userProfileGroup, @NonNull List<SafetySourcesGroup> safetySourcesGroups) { - List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories = new ArrayList<>(); + List<SafetyCenterIssueExtended> safetyCenterIssuesExtended = new ArrayList<>(); List<SafetyCenterEntryOrGroup> safetyCenterEntryOrGroups = new ArrayList<>(); List<SafetyCenterStaticEntryGroup> safetyCenterStaticEntryGroups = new ArrayList<>(); SafetyCenterOverallState safetyCenterOverallState = new SafetyCenterOverallState(); @@ -150,14 +160,10 @@ final class SafetyCenterDataFactory { for (int i = 0; i < safetySourcesGroups.size(); i++) { SafetySourcesGroup safetySourcesGroup = safetySourcesGroups.get(i); - addSafetyCenterIssues( - safetyCenterOverallState, - safetyCenterIssuesWithCategories, - safetySourcesGroup, - userProfileGroup); + addSafetyCenterIssues(safetyCenterIssuesExtended, safetySourcesGroup, userProfileGroup); int safetySourcesGroupType = safetySourcesGroup.getType(); switch (safetySourcesGroupType) { - case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE: + case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL: addSafetyCenterEntryGroup( safetyCenterOverallState, safetyCenterEntryOrGroups, @@ -165,7 +171,7 @@ final class SafetyCenterDataFactory { packageName, userProfileGroup); break; - case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID: + case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS: addSafetyCenterStaticEntryGroup( safetyCenterOverallState, safetyCenterStaticEntryGroups, @@ -181,20 +187,37 @@ final class SafetyCenterDataFactory { } } - safetyCenterIssuesWithCategories.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING); + safetyCenterIssuesExtended.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING); - List<SafetyCenterIssue> safetyCenterIssues = - new ArrayList<>(safetyCenterIssuesWithCategories.size()); - for (int i = 0; i < safetyCenterIssuesWithCategories.size(); i++) { - safetyCenterIssues.add(safetyCenterIssuesWithCategories.get(i).getSafetyCenterIssue()); + if (SdkLevel.isAtLeastU() && mSafetyCenterIssueDeduplicator != null) { + mSafetyCenterIssueDeduplicator.deduplicateIssues(safetyCenterIssuesExtended); + } + + List<SafetyCenterIssue> safetyCenterIssues = new ArrayList<>(); + List<SafetyCenterIssue> safetyCenterDismissedIssues = new ArrayList<>(); + SafetyCenterIssueExtended topNonDismissedIssueExtended = null; + + for (int i = 0; i < safetyCenterIssuesExtended.size(); i++) { + SafetyCenterIssueExtended issueExtended = safetyCenterIssuesExtended.get(i); + if (mSafetyCenterIssueCache.isIssueDismissed(issueExtended)) { + safetyCenterDismissedIssues.add(issueExtended.getSafetyCenterIssue()); + } else { + safetyCenterIssues.add(issueExtended.getSafetyCenterIssue()); + safetyCenterOverallState.addIssueOverallSeverityLevel( + toSafetyCenterStatusOverallSeverityLevel( + issueExtended.getSafetySourceIssueSeverityLevel())); + if (topNonDismissedIssueExtended == null) { + topNonDismissedIssueExtended = issueExtended; + } + } } int refreshStatus = mSafetyCenterRefreshTracker.getRefreshStatus(); - return new SafetyCenterData( + SafetyCenterStatus safetyCenterStatus = new SafetyCenterStatus.Builder( getSafetyCenterStatusTitle( safetyCenterOverallState.getOverallSeverityLevel(), - safetyCenterIssuesWithCategories, + topNonDismissedIssueExtended, refreshStatus, safetyCenterOverallState.hasSettingsToReview()), getSafetyCenterStatusSummary( @@ -204,10 +227,22 @@ final class SafetyCenterDataFactory { safetyCenterOverallState.hasSettingsToReview())) .setSeverityLevel(safetyCenterOverallState.getOverallSeverityLevel()) .setRefreshStatus(refreshStatus) - .build(), - safetyCenterIssues, - safetyCenterEntryOrGroups, - safetyCenterStaticEntryGroups); + .build(); + + if (SdkLevel.isAtLeastU()) { + return new SafetyCenterData( + safetyCenterStatus, + safetyCenterIssues, + safetyCenterEntryOrGroups, + safetyCenterStaticEntryGroups, + safetyCenterDismissedIssues); + } else { + return new SafetyCenterData( + safetyCenterStatus, + safetyCenterIssues, + safetyCenterEntryOrGroups, + safetyCenterStaticEntryGroups); + } } @NonNull @@ -216,8 +251,7 @@ final class SafetyCenterDataFactory { } private void addSafetyCenterIssues( - @NonNull SafetyCenterOverallState safetyCenterOverallState, - @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories, + @NonNull List<SafetyCenterIssueExtended> safetyCenterIssues, @NonNull SafetySourcesGroup safetySourcesGroup, @NonNull UserProfileGroup userProfileGroup) { List<SafetySource> safetySources = safetySourcesGroup.getSafetySources(); @@ -229,9 +263,9 @@ final class SafetyCenterDataFactory { } addSafetyCenterIssues( - safetyCenterOverallState, - safetyCenterIssuesWithCategories, + safetyCenterIssues, safetySource, + safetySourcesGroup, userProfileGroup.getProfileParentUserId()); if (!SafetySources.supportsManagedProfiles(safetySource)) { @@ -244,18 +278,18 @@ final class SafetyCenterDataFactory { int managedRunningProfileUserId = managedRunningProfilesUserIds[j]; addSafetyCenterIssues( - safetyCenterOverallState, - safetyCenterIssuesWithCategories, + safetyCenterIssues, safetySource, + safetySourcesGroup, managedRunningProfileUserId); } } } private void addSafetyCenterIssues( - @NonNull SafetyCenterOverallState safetyCenterOverallState, - @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories, + @NonNull List<SafetyCenterIssueExtended> safetyCenterIssues, @NonNull SafetySource safetySource, + @NonNull SafetySourcesGroup safetySourcesGroup, @UserIdInt int userId) { SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); SafetySourceData safetySourceData = mSafetyCenterRepository.getSafetySourceData(key); @@ -268,17 +302,19 @@ final class SafetyCenterDataFactory { for (int i = 0; i < safetySourceIssues.size(); i++) { SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); SafetyCenterIssue safetyCenterIssue = - toSafetyCenterIssue(safetySourceIssue, safetySource, userId); - - if (safetyCenterIssue == null) { - continue; + toSafetyCenterIssue( + safetySourceIssue, safetySource, safetySourcesGroup, userId); + + SafetyCenterIssueExtended.Builder issueExtendedBuilder = + new SafetyCenterIssueExtended.Builder( + safetyCenterIssue, + safetySourceIssue.getIssueCategory(), + safetySourceIssue.getSeverityLevel()); + if (SdkLevel.isAtLeastU()) { + issueExtendedBuilder.setDeduplicationGroup(safetySource.getDeduplicationGroup()); + issueExtendedBuilder.setDeduplicationId(safetySourceIssue.getDeduplicationId()); } - - safetyCenterOverallState.addIssueOverallSeverityLevel( - toSafetyCenterStatusOverallSeverityLevel(safetySourceIssue.getSeverityLevel())); - safetyCenterIssuesWithCategories.add( - SafetyCenterIssueWithCategory.create( - safetyCenterIssue, safetySourceIssue.getIssueCategory())); + safetyCenterIssues.add(issueExtendedBuilder.build()); } } @@ -286,6 +322,7 @@ final class SafetyCenterDataFactory { private SafetyCenterIssue toSafetyCenterIssue( @NonNull SafetySourceIssue safetySourceIssue, @NonNull SafetySource safetySource, + @NonNull SafetySourcesGroup safetySourcesGroup, @UserIdInt int userId) { SafetyCenterIssueId safetyCenterIssueId = SafetyCenterIssueId.newBuilder() @@ -298,12 +335,6 @@ final class SafetyCenterDataFactory { .setIssueTypeId(safetySourceIssue.getIssueTypeId()) .build(); - if (mSafetyCenterIssueCache.isIssueDismissed( - safetyCenterIssueId.getSafetyCenterIssueKey(), - safetySourceIssue.getSeverityLevel())) { - return null; - } - List<SafetySourceIssue.Action> safetySourceIssueActions = safetySourceIssue.getActions(); List<SafetyCenterIssue.Action> safetyCenterIssueActions = new ArrayList<>(safetySourceIssueActions.size()); @@ -318,16 +349,26 @@ final class SafetyCenterDataFactory { int safetyCenterIssueSeverityLevel = toSafetyCenterIssueSeverityLevel(safetySourceIssue.getSeverityLevel()); - return new SafetyCenterIssue.Builder( - SafetyCenterIds.encodeToString(safetyCenterIssueId), - safetySourceIssue.getTitle(), - safetySourceIssue.getSummary()) - .setSeverityLevel(safetyCenterIssueSeverityLevel) - .setShouldConfirmDismissal( - safetyCenterIssueSeverityLevel > SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) - .setSubtitle(safetySourceIssue.getSubtitle()) - .setActions(safetyCenterIssueActions) - .build(); + SafetyCenterIssue.Builder safetyCenterIssueBuilder = + new SafetyCenterIssue.Builder( + SafetyCenterIds.encodeToString(safetyCenterIssueId), + safetySourceIssue.getTitle(), + safetySourceIssue.getSummary()) + .setSeverityLevel(safetyCenterIssueSeverityLevel) + .setShouldConfirmDismissal( + safetyCenterIssueSeverityLevel + > SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) + .setSubtitle(safetySourceIssue.getSubtitle()) + .setActions(safetyCenterIssueActions); + if (SdkLevel.isAtLeastU()) { + CharSequence issueAttributionTitle = + TextUtils.isEmpty(safetySourceIssue.getAttributionTitle()) + ? mSafetyCenterResourcesContext.getOptionalString( + safetySourcesGroup.getTitleResId()) + : safetySourceIssue.getAttributionTitle(); + safetyCenterIssueBuilder.setAttributionTitle(issueAttributionTitle); + } + return safetyCenterIssueBuilder.build(); } @NonNull @@ -407,7 +448,7 @@ final class SafetyCenterDataFactory { return; } - if (entries.size() == 1) { + if (!SafetyCenterFlags.getShowSubpages() && entries.size() == 1) { safetyCenterEntryOrGroups.add(new SafetyCenterEntryOrGroup(entries.get(0))); return; } @@ -634,7 +675,7 @@ final class SafetyCenterDataFactory { case SafetySource.SAFETY_SOURCE_TYPE_STATIC: return toDefaultSafetyCenterEntry( safetySource, - defaultPackageName, + getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName), SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED, SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON, userId, @@ -815,7 +856,7 @@ final class SafetyCenterDataFactory { case SafetySource.SAFETY_SOURCE_TYPE_STATIC: return toDefaultSafetyCenterStaticEntry( safetySource, - defaultPackageName, + getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName), userId, isUserManaged, isManagedUserRunning); @@ -881,6 +922,19 @@ final class SafetyCenterDataFactory { return safetySourceData.getStatus(); } + @NonNull + private static String getStaticSourcePackageNameOrDefault( + @NonNull SafetySource safetySource, @NonNull String defaultPackageName) { + if (!SdkLevel.isAtLeastU()) { + return defaultPackageName; + } + String sourcePackageName = safetySource.getOptionalPackageName(); + if (sourcePackageName == null) { + return defaultPackageName; + } + return sourcePackageName; + } + @SafetyCenterStatus.OverallSeverityLevel private static int toSafetyCenterStatusOverallSeverityLevel( @SafetySourceData.SeverityLevel int safetySourceSeverityLevel) { @@ -1000,7 +1054,7 @@ final class SafetyCenterDataFactory { @NonNull private String getSafetyCenterStatusTitle( @SafetyCenterStatus.OverallSeverityLevel int overallSeverityLevel, - @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories, + @Nullable SafetyCenterIssueExtended topNonDismissedIssueExtended, @SafetyCenterStatus.RefreshStatus int refreshStatus, boolean hasSettingsToReview) { boolean overallSeverityUnknown = @@ -1021,16 +1075,22 @@ final class SafetyCenterDataFactory { "overall_severity_level_ok_title"); case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION: return getStatusTitleFromIssueCategories( - safetyCenterIssuesWithCategories, + topNonDismissedIssueExtended, "overall_severity_level_device_recommendation_title", "overall_severity_level_account_recommendation_title", - "overall_severity_level_safety_recommendation_title"); + "overall_severity_level_safety_recommendation_title", + "overall_severity_level_data_recommendation_title", + "overall_severity_level_passwords_recommendation_title", + "overall_severity_level_personal_recommendation_title"); case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING: return getStatusTitleFromIssueCategories( - safetyCenterIssuesWithCategories, + topNonDismissedIssueExtended, "overall_severity_level_critical_device_warning_title", "overall_severity_level_critical_account_warning_title", - "overall_severity_level_critical_safety_warning_title"); + "overall_severity_level_critical_safety_warning_title", + "overall_severity_level_critical_data_warning_title", + "overall_severity_level_critical_passwords_warning_title", + "overall_severity_level_critical_personal_warning_title"); } Log.w(TAG, "Unexpected SafetyCenterStatus.OverallSeverityLevel: " + overallSeverityLevel); @@ -1039,16 +1099,19 @@ final class SafetyCenterDataFactory { @NonNull private String getStatusTitleFromIssueCategories( - @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories, + @Nullable SafetyCenterIssueExtended topNonDismissedIssueExtended, @NonNull String deviceResourceName, @NonNull String accountResourceName, - @NonNull String generalResourceName) { + @NonNull String generalResourceName, + @NonNull String dataResourceName, + @NonNull String passwordsResourceName, + @NonNull String personalSafetyResourceName) { String generalString = mSafetyCenterResourcesContext.getStringByName(generalResourceName); - if (safetyCenterIssuesWithCategories.isEmpty()) { + if (topNonDismissedIssueExtended == null) { Log.w(TAG, "No safety center issues found in a non-green status"); return generalString; } - int issueCategory = safetyCenterIssuesWithCategories.get(0).getSafetyCenterIssueCategory(); + int issueCategory = topNonDismissedIssueExtended.getSafetySourceIssueCategory(); switch (issueCategory) { case SafetySourceIssue.ISSUE_CATEGORY_DEVICE: return mSafetyCenterResourcesContext.getStringByName(deviceResourceName); @@ -1057,6 +1120,17 @@ final class SafetyCenterDataFactory { case SafetySourceIssue.ISSUE_CATEGORY_GENERAL: return generalString; } + if (SdkLevel.isAtLeastU()) { + switch (issueCategory) { + case SafetySourceIssue.ISSUE_CATEGORY_DATA: + return mSafetyCenterResourcesContext.getStringByName(dataResourceName); + case SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS: + return mSafetyCenterResourcesContext.getStringByName(passwordsResourceName); + case SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY: + return mSafetyCenterResourcesContext.getStringByName( + personalSafetyResourceName); + } + } Log.w(TAG, "Unexpected SafetySourceIssue.IssueCategory: " + issueCategory); return generalString; @@ -1149,45 +1223,15 @@ final class SafetyCenterDataFactory { return SafetySourceKey.of(id.getSafetySourceId(), id.getUserId()); } - /** Wrapper that encapsulates both {@link SafetyCenterIssue} and its category. */ - private static final class SafetyCenterIssueWithCategory { - @NonNull private final SafetyCenterIssue mSafetyCenterIssue; - @SafetySourceIssue.IssueCategory private final int mSafetyCenterIssueCategory; - - private SafetyCenterIssueWithCategory( - @NonNull SafetyCenterIssue safetyCenterIssue, - @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) { - this.mSafetyCenterIssue = safetyCenterIssue; - this.mSafetyCenterIssueCategory = safetyCenterIssueCategory; - } - - @NonNull - private SafetyCenterIssue getSafetyCenterIssue() { - return mSafetyCenterIssue; - } - - @SafetySourceIssue.IssueCategory - private int getSafetyCenterIssueCategory() { - return mSafetyCenterIssueCategory; - } - - private static SafetyCenterIssueWithCategory create( - @NonNull SafetyCenterIssue safetyCenterIssue, - @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) { - return new SafetyCenterIssueWithCategory(safetyCenterIssue, safetyCenterIssueCategory); - } - } - - /** A comparator to order {@link SafetyCenterIssueWithCategory} by severity level descending. */ + /** A comparator to order {@link SafetyCenterIssueExtended} by severity level descending. */ private static final class SafetyCenterIssuesBySeverityDescending - implements Comparator<SafetyCenterIssueWithCategory> { + implements Comparator<SafetyCenterIssueExtended> { private SafetyCenterIssuesBySeverityDescending() {} @Override public int compare( - @NonNull SafetyCenterIssueWithCategory left, - @NonNull SafetyCenterIssueWithCategory right) { + @NonNull SafetyCenterIssueExtended left, @NonNull SafetyCenterIssueExtended right) { return Integer.compare( right.getSafetyCenterIssue().getSeverityLevel(), left.getSafetyCenterIssue().getSeverityLevel()); diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java index 3ce2a89aa..0e508fe5b 100644 --- a/service/java/com/android/safetycenter/SafetyCenterFlags.java +++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java @@ -30,6 +30,8 @@ import android.util.Log; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.io.PrintWriter; import java.time.Duration; @@ -80,6 +82,11 @@ final class SafetyCenterFlags { private static final String PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS = "safety_center_allow_statsd_logging_in_tests"; + private static final String PROPERTY_SHOW_SUBPAGES = "safety_center_show_subpages"; + + private static final String PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES = + "safety_center_override_refresh_on_page_open_sources"; + private static final Duration REFRESH_SOURCES_TIMEOUT_DEFAULT_DURATION = Duration.ofSeconds(15); private static final Duration RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION = @@ -112,6 +119,11 @@ final class SafetyCenterFlags { fout, PROPERTY_REFRESH_SOURCES_TIMEOUTS_MILLIS, getRefreshSourcesTimeoutsMillis()); printFlag(fout, PROPERTY_ISSUE_CATEGORY_ALLOWLISTS, getIssueCategoryAllowlists()); printFlag(fout, PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS, getAllowStatsdLoggingInTests()); + printFlag(fout, PROPERTY_SHOW_SUBPAGES, getShowSubpages()); + printFlag( + fout, + PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES, + getOverrideRefreshOnPageOpenSourceIds()); fout.println(); } @@ -314,6 +326,23 @@ final class SafetyCenterFlags { return getBoolean(PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS, false); } + /** + * Returns whether to show subpages in the Safety Center UI for Android-U instead of the + * expand-and-collapse list implementation. + */ + static boolean getShowSubpages() { + // TODO(b/260822348): Add CTS test to verify that the flag is disabled when turned on for T + return SdkLevel.isAtLeastU() && getBoolean(PROPERTY_SHOW_SUBPAGES, true); + } + + /** + * Returns an array of safety source Ids that will be refreshed on page open, even if + * refreshOnPageOpenAllowed is false (the default) in the XML config. + */ + static ArraySet<String> getOverrideRefreshOnPageOpenSourceIds() { + return getCommaSeparatedStrings(PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES); + } + @NonNull private static Duration getDuration(@NonNull String property, @NonNull Duration defaultValue) { return Duration.ofMillis(getLong(property, defaultValue.toMillis())); diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueCache.java b/service/java/com/android/safetycenter/SafetyCenterIssueCache.java index 388ddf89d..ed3d997bf 100644 --- a/service/java/com/android/safetycenter/SafetyCenterIssueCache.java +++ b/service/java/com/android/safetycenter/SafetyCenterIssueCache.java @@ -111,6 +111,13 @@ final class SafetyCenterIssueCache { return result; } + /** Same as {@link SafetyCenterIssueCache#isIssueDismissed(SafetyCenterIssueKey, int)}. */ + boolean isIssueDismissed(@NonNull SafetyCenterIssueExtended safetyCenterIssue) { + return isIssueDismissed( + safetyCenterIssue.getSafetyCenterIssueKey(), + safetyCenterIssue.getSafetySourceIssueSeverityLevel()); + } + /** * Returns {@code true} if the issue with the given key and severity level is currently * dismissed. @@ -172,6 +179,25 @@ final class SafetyCenterIssueCache { } /** + * Copy dismissal data from one issue to the other. + * + * <p>This will align dismissal state of these issues, unless issues are of different + * severities, in which case they can potentially differ in resurface times. + */ + void copyDismissalData( + @NonNull SafetyCenterIssueKey keyFrom, @NonNull SafetyCenterIssueKey keyTo) { + IssueData dataFrom = getOrWarn(keyFrom, "copying dismissed data"); + IssueData dataTo = getOrWarn(keyTo, "copying dismissed data"); + if (dataFrom == null || dataTo == null) { + return; + } + + dataTo.setDismissedAt(dataFrom.getDismissedAt()); + dataTo.setDismissCount(dataFrom.getDismissCount()); + mIsDirty = true; + } + + /** * Marks the notification (if any) of the issue with the given key as dismissed. * * <p>The issue itself is <strong>not</strong> marked as dismissed and its warning card can diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java b/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java new file mode 100644 index 000000000..50a8f0e51 --- /dev/null +++ b/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java @@ -0,0 +1,204 @@ +/* + * 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.safetycenter; + +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; + +import android.annotation.NonNull; +import android.util.ArrayMap; +import android.util.ArraySet; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import com.android.safetycenter.internaldata.SafetyCenterIssueKey; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import javax.annotation.concurrent.NotThreadSafe; + +/** Deduplicates issues based on deduplication info provided by the source and the issue. */ +@RequiresApi(UPSIDE_DOWN_CAKE) +@NotThreadSafe +final class SafetyCenterIssueDeduplicator { + + @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache; + + SafetyCenterIssueDeduplicator(@NonNull SafetyCenterIssueCache safetyCenterIssueCache) { + this.mSafetyCenterIssueCache = safetyCenterIssueCache; + } + + /** + * Accepts a list of issues sorted by priority and filters out duplicates. + * + * <p>Issues are considered duplicate if they have the same deduplication id and were sent by + * sources which are part of the same deduplication group. All but the highest priority + * duplicate issue will be filtered out. + * + * <p>In case any issue, in the bucket of duplicate issues, was dismissed, all issues of the + * same or lower severity will be dismissed as well. + * + * <p>This method modifies the given argument. + */ + void deduplicateIssues(@NonNull List<SafetyCenterIssueExtended> sortedIssues) { + // (dedup key) -> list(issues) + ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets = + createDedupBuckets(sortedIssues); + + dismissDuplicateIssuesOfDismissedIssue(dedupBuckets); + + ArraySet<SafetyCenterIssueKey> duplicatesToFilterOut = + getDuplicatesToFilterOut(dedupBuckets); + + Iterator<SafetyCenterIssueExtended> it = sortedIssues.iterator(); + while (it.hasNext()) { + if (duplicatesToFilterOut.contains(it.next().getSafetyCenterIssueKey())) { + it.remove(); + } + } + } + + /** + * Handles dismissals logic: in each bucket, dismissal details of the top (highest priority) + * dismissed issue will be copied to all other duplicate issues in that bucket, that are of + * equal or lower severity (not priority). + */ + private void dismissDuplicateIssuesOfDismissedIssue( + @NonNull ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets) { + for (int i = 0; i < dedupBuckets.size(); i++) { + List<SafetyCenterIssueExtended> duplicates = dedupBuckets.valueAt(i); + SafetyCenterIssueExtended topDismissed = getHighestPriorityDismissedIssue(duplicates); + alignDismissalsDataWithinBucket(topDismissed, duplicates); + } + } + + /** + * Dismisses all issues of lower or equal severity relative to the given top dismissed issue in + * the bucket. + */ + private void alignDismissalsDataWithinBucket( + @Nullable SafetyCenterIssueExtended topDismissed, + @NonNull List<SafetyCenterIssueExtended> duplicates) { + if (topDismissed == null) { + return; + } + SafetyCenterIssueKey topDismissedIssueKey = topDismissed.getSafetyCenterIssueKey(); + int topDismissedSeverityLevel = topDismissed.getSafetyCenterIssue().getSeverityLevel(); + for (int i = 0; i < duplicates.size(); i++) { + SafetyCenterIssueExtended issue = duplicates.get(i); + SafetyCenterIssueKey issueKey = issue.getSafetyCenterIssueKey(); + if (!issueKey.equals(topDismissedIssueKey) + && issue.getSafetyCenterIssue().getSeverityLevel() + <= topDismissedSeverityLevel) { + // all duplicate issues should have same dismissals data as top dismissed issue + mSafetyCenterIssueCache.copyDismissalData(topDismissedIssueKey, issueKey); + } + } + } + + @Nullable + private SafetyCenterIssueExtended getHighestPriorityDismissedIssue( + @NonNull List<SafetyCenterIssueExtended> duplicates) { + for (int i = 0; i < duplicates.size(); i++) { + SafetyCenterIssueExtended issue = duplicates.get(i); + if (mSafetyCenterIssueCache.isIssueDismissed(issue)) { + return issue; + } + } + + return null; + } + + /** Returns a set of duplicate issues that need to be filtered out. */ + @NonNull + private static ArraySet<SafetyCenterIssueKey> getDuplicatesToFilterOut( + @NonNull ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets) { + ArraySet<SafetyCenterIssueKey> duplicatesToFilterOut = new ArraySet<>(); + + for (int i = 0; i < dedupBuckets.size(); i++) { + List<SafetyCenterIssueExtended> duplicates = dedupBuckets.valueAt(i); + // all but the top one in the bucket + for (int j = 1; j < duplicates.size(); j++) { + duplicatesToFilterOut.add(duplicates.get(j).getSafetyCenterIssueKey()); + } + } + + return duplicatesToFilterOut; + } + + /** Returns a mapping (dedup key) -> list(issues). */ + @NonNull + private static ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> createDedupBuckets( + @NonNull List<SafetyCenterIssueExtended> sortedIssues) { + ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets = new ArrayMap<>(); + + for (int i = 0; i < sortedIssues.size(); i++) { + SafetyCenterIssueExtended issue = sortedIssues.get(i); + DeduplicationKey dedupKey = getDedupKey(issue); + if (dedupKey == null) { + continue; + } + + // each bucket will remain sorted + List<SafetyCenterIssueExtended> bucket = + dedupBuckets.getOrDefault(dedupKey, new ArrayList<>()); + bucket.add(issue); + + dedupBuckets.put(dedupKey, bucket); + } + + return dedupBuckets; + } + + /** Returns deduplication key of the given issue. */ + @Nullable + private static DeduplicationKey getDedupKey(@NonNull SafetyCenterIssueExtended issue) { + if (issue.getDeduplicationGroup() == null || issue.getDeduplicationId() == null) { + return null; + } + return new DeduplicationKey(issue.getDeduplicationGroup(), issue.getDeduplicationId()); + } + + private static class DeduplicationKey { + + @NonNull private final String mDeduplicationGroup; + @NonNull private final String mDeduplicationId; + + private DeduplicationKey( + @NonNull String deduplicationGroup, @NonNull String deduplicationId) { + mDeduplicationGroup = deduplicationGroup; + mDeduplicationId = deduplicationId; + } + + @Override + public int hashCode() { + return Objects.hash(mDeduplicationGroup, mDeduplicationId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DeduplicationKey)) return false; + DeduplicationKey dedupKey = (DeduplicationKey) o; + return mDeduplicationGroup.equals(dedupKey.mDeduplicationGroup) + && mDeduplicationId.equals(dedupKey.mDeduplicationId); + } + } +} diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java b/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java new file mode 100644 index 000000000..60e0b5bd3 --- /dev/null +++ b/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java @@ -0,0 +1,144 @@ +/* + * 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.safetycenter; + +import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.safetycenter.SafetyCenterIssue; +import android.safetycenter.SafetySourceData; +import android.safetycenter.SafetySourceIssue; + +import androidx.annotation.RequiresApi; + +import com.android.safetycenter.internaldata.SafetyCenterIds; +import com.android.safetycenter.internaldata.SafetyCenterIssueKey; + +import javax.annotation.concurrent.NotThreadSafe; + +/** Wrapper that contains a {@link SafetyCenterIssue} and some extra information about it. */ +@RequiresApi(TIRAMISU) +final class SafetyCenterIssueExtended { + @NonNull private final SafetyCenterIssue mSafetyCenterIssue; + @NonNull private final SafetyCenterIssueKey mSafetyCenterIssueKey; + @SafetySourceIssue.IssueCategory private final int mSafetySourceIssueCategory; + @SafetySourceData.SeverityLevel private final int mSafetySourceIssueSeverityLevel; + + // Deduplication info, only available on Android U+. + @Nullable private final String mDeduplicationGroup; + @Nullable private final String mDeduplicationId; + + private SafetyCenterIssueExtended( + @NonNull SafetyCenterIssue safetyCenterIssue, + @NonNull SafetyCenterIssueKey safetyCenterIssueKey, + @SafetySourceIssue.IssueCategory int safetySourceIssueCategory, + int safetySourceIssueSeverityLevel, + @Nullable String deduplicationGroup, + @Nullable String deduplicationId) { + this.mSafetyCenterIssue = safetyCenterIssue; + this.mSafetyCenterIssueKey = safetyCenterIssueKey; + this.mSafetySourceIssueCategory = safetySourceIssueCategory; + this.mSafetySourceIssueSeverityLevel = safetySourceIssueSeverityLevel; + this.mDeduplicationGroup = deduplicationGroup; + this.mDeduplicationId = deduplicationId; + } + + /** Returns the {@link SafetyCenterIssue} it contains. */ + @NonNull + SafetyCenterIssue getSafetyCenterIssue() { + return mSafetyCenterIssue; + } + + /** Returns the {@link SafetyCenterIssueKey} related to this issue. */ + @NonNull + SafetyCenterIssueKey getSafetyCenterIssueKey() { + return mSafetyCenterIssueKey; + } + + /** Returns the {@link SafetySourceIssue.IssueCategory} related to this issue. */ + @SafetySourceIssue.IssueCategory + int getSafetySourceIssueCategory() { + return mSafetySourceIssueCategory; + } + + /** Returns the {@link SafetySourceData.SeverityLevel} related to this issue. */ + @SafetySourceData.SeverityLevel + int getSafetySourceIssueSeverityLevel() { + return mSafetySourceIssueSeverityLevel; + } + + /** Returns the deduplication group related to this issue. */ + @Nullable + String getDeduplicationGroup() { + return mDeduplicationGroup; + } + + /** Returns the deduplication id related to this issue. */ + @Nullable + String getDeduplicationId() { + return mDeduplicationId; + } + + /** Builder for the {@link SafetyCenterIssueExtended}. */ + @NotThreadSafe + static final class Builder { + @NonNull private final SafetyCenterIssue mSafetyCenterIssue; + @SafetySourceIssue.IssueCategory private final int mSafetySourceIssueCategory; + @SafetySourceData.SeverityLevel private final int mSafetySourceIssueSeverityLevel; + + @Nullable private String mDeduplicationGroup; + @Nullable private String mDeduplicationId; + + /** Constructs a new instance of the builder. */ + Builder( + @NonNull SafetyCenterIssue safetyCenterIssue, + @SafetySourceIssue.IssueCategory int safetySourceIssueCategory, + @SafetySourceData.SeverityLevel int safetySourceIssueSeverityLevel) { + this.mSafetyCenterIssue = safetyCenterIssue; + this.mSafetySourceIssueCategory = safetySourceIssueCategory; + this.mSafetySourceIssueSeverityLevel = safetySourceIssueSeverityLevel; + } + + /** Sets the deduplication group for this issue. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + Builder setDeduplicationGroup(@Nullable String deduplicationGroup) { + this.mDeduplicationGroup = deduplicationGroup; + return this; + } + + /** Sets the deduplication id for this issue. */ + @RequiresApi(UPSIDE_DOWN_CAKE) + Builder setDeduplicationId(@Nullable String deduplicationId) { + this.mDeduplicationId = deduplicationId; + return this; + } + + /** Returns a new {@link SafetyCenterIssueExtended} based on previously given data. */ + SafetyCenterIssueExtended build() { + return new SafetyCenterIssueExtended( + mSafetyCenterIssue, + SafetyCenterIds.issueIdFromString(mSafetyCenterIssue.getId()) + .getSafetyCenterIssueKey(), + mSafetySourceIssueCategory, + mSafetySourceIssueSeverityLevel, + mDeduplicationGroup, + mDeduplicationId); + } + } +} diff --git a/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java b/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java index 909ff6ec8..0910750d3 100644 --- a/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java +++ b/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java @@ -36,6 +36,7 @@ import android.util.Log; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; import com.android.safetycenter.internaldata.SafetyCenterIssueKey; import java.io.PrintWriter; @@ -94,6 +95,8 @@ final class SafetyCenterNotificationSender { @NonNull private final SafetyCenterRepository mSafetyCenterRepository; + @NonNull private final SafetyCenterConfigReader mSafetyCenterConfigReader; + private final ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> mNotifiedIssues = new ArrayMap<>(); @@ -101,11 +104,13 @@ final class SafetyCenterNotificationSender { @NonNull Context context, @NonNull SafetyCenterNotificationFactory notificationFactory, @NonNull SafetyCenterIssueCache issueCache, - @NonNull SafetyCenterRepository safetyCenterRepository) { + @NonNull SafetyCenterRepository safetyCenterRepository, + @NonNull SafetyCenterConfigReader safetyCenterConfigReader) { mContext = context; mNotificationFactory = notificationFactory; mIssueCache = issueCache; mSafetyCenterRepository = safetyCenterRepository; + mSafetyCenterConfigReader = safetyCenterConfigReader; } /** @@ -210,6 +215,16 @@ final class SafetyCenterNotificationSender { @NotificationBehaviorInternal private int getBehavior( @NonNull SafetySourceIssue issue, @NonNull SafetyCenterIssueKey issueKey) { + if (SdkLevel.isAtLeastU()) { + switch (issue.getNotificationBehavior()) { + case SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER: + return NOTIFICATION_BEHAVIOR_INTERNAL_NEVER; + case SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED: + return NOTIFICATION_BEHAVIOR_INTERNAL_DELAYED; + case SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY: + return NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY; + } + } // On Android T all issues are assumed to have "unspecified" behavior return getBehaviorForIssueWithUnspecifiedBehavior(issueKey); } @@ -221,6 +236,14 @@ final class SafetyCenterNotificationSender { } private boolean areNotificationsAllowed(@NonNull String sourceId) { + if (SdkLevel.isAtLeastU()) { + SafetyCenterConfigReader.ExternalSafetySource externalSafetySource = + mSafetyCenterConfigReader.getExternalSafetySource(sourceId); + if (externalSafetySource != null + && externalSafetySource.getSafetySource().areNotificationsAllowed()) { + return true; + } + } return SafetyCenterFlags.getNotificationsAllowedSourceIds().contains(sourceId); } diff --git a/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java b/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java index 479509322..d022a0db7 100644 --- a/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java +++ b/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java @@ -39,6 +39,7 @@ import android.util.StatsEvent; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; +import com.android.modules.utils.build.SdkLevel; import com.android.permission.PermissionStatsLog; import com.android.safetycenter.internaldata.SafetyCenterIssueKey; @@ -139,15 +140,24 @@ final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback { mSafetyCenterDataFactory.assembleSafetyCenterData( "android", userProfileGroup, loggableGroups); long openIssuesCount = loggableData.getIssues().size(); - long dismissedIssuesCount = - mSafetyCenterIssueCache.countActiveLoggableIssues(userProfileGroup) - - openIssuesCount; + long dismissedIssuesCount = getDismissedIssuesCountLocked(loggableData, userProfileGroup); return mStatsdLogger.createSafetyStateEvent( loggableData.getStatus().getSeverityLevel(), openIssuesCount, dismissedIssuesCount); } @GuardedBy("mApiLock") + private long getDismissedIssuesCountLocked( + @NonNull SafetyCenterData loggableData, @NonNull UserProfileGroup userProfileGroup) { + if (SdkLevel.isAtLeastU()) { + return loggableData.getDismissedIssues().size(); + } + long openIssuesCount = loggableData.getIssues().size(); + return mSafetyCenterIssueCache.countActiveLoggableIssues(userProfileGroup) + - openIssuesCount; + } + + @GuardedBy("mApiLock") private void writeSafetySourceStateCollectedAtomsLocked( @NonNull UserProfileGroup userProfileGroup, @NonNull List<SafetySourcesGroup> loggableGroups) { diff --git a/service/java/com/android/safetycenter/SafetyCenterRepository.java b/service/java/com/android/safetycenter/SafetyCenterRepository.java index c42d849ad..eb28ed4cf 100644 --- a/service/java/com/android/safetycenter/SafetyCenterRepository.java +++ b/service/java/com/android/safetycenter/SafetyCenterRepository.java @@ -25,6 +25,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.os.SystemClock; import android.safetycenter.SafetyCenterData; import android.safetycenter.SafetyEvent; @@ -40,6 +42,7 @@ import android.util.Log; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; import com.android.permission.util.UserUtils; import com.android.safetycenter.internaldata.SafetyCenterIssueActionId; import com.android.safetycenter.internaldata.SafetyCenterIssueKey; @@ -48,6 +51,7 @@ import java.io.PrintWriter; import java.time.Duration; import java.util.List; import java.util.Objects; +import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; @@ -76,6 +80,7 @@ final class SafetyCenterRepository { @NonNull private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker; @NonNull private final StatsdLogger mStatsdLogger; @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache; + @NonNull private final PackageManager mPackageManager; SafetyCenterRepository( @NonNull Context context, @@ -88,6 +93,7 @@ final class SafetyCenterRepository { mSafetyCenterRefreshTracker = safetyCenterRefreshTracker; mStatsdLogger = statsdLogger; mSafetyCenterIssueCache = safetyCenterIssueCache; + mPackageManager = mContext.getPackageManager(); } /** @@ -444,17 +450,7 @@ final class SafetyCenterRepository { } SafetySource safetySource = externalSafetySource.getSafetySource(); - - // TODO(b/222330089): Security: check certs? - if (!packageName.equals(safetySource.getPackageName())) { - throw new IllegalArgumentException( - "Unexpected package name: " - + packageName - + ", for safety source: " - + safetySourceId); - } - - // TODO(b/222327845): Security: check package is installed for user? + validateCallingPackage(safetySource, packageName, safetySourceId); if (UserUtils.isManagedProfile(userId, mContext) && !SafetySources.supportsManagedProfiles(safetySource)) { @@ -484,12 +480,12 @@ final class SafetyCenterRepository { if (safetySourceStatus != null) { int sourceSeverityLevel = safetySourceStatus.getSeverityLevel(); - if (externalSafetySource.hasEntryInRigidGroup() + if (externalSafetySource.hasEntryInStatelessGroup() && sourceSeverityLevel != SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED) { throw new IllegalArgumentException( "Safety source: " + safetySourceId - + " is in a rigid group but specified a severity level: " + + " is in a stateless group but specified a severity level: " + sourceSeverityLevel); } @@ -533,6 +529,59 @@ final class SafetyCenterRepository { return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId); } + private void validateCallingPackage( + @NonNull SafetySource safetySource, + @NonNull String packageName, + @NonNull String safetySourceId) { + if (!packageName.equals(safetySource.getPackageName())) { + throw new IllegalArgumentException( + "Unexpected package name: " + + packageName + + ", for safety source: " + + safetySourceId); + } + + // TODO(b/222327845): Security: check package is installed for user? + + if (!SdkLevel.isAtLeastU()) { + // No more validation checks possible on T devices + return; + } + + Set<String> certificateHashes = safetySource.getPackageCertificateHashes(); + if (certificateHashes.isEmpty()) { + Log.d(TAG, "No cert check requested for package " + packageName); + return; + } + + boolean hasMatchingCert = false; + for (String certHash : certificateHashes) { + try { + byte[] certificate = new Signature(certHash).toByteArray(); + if (mPackageManager.hasSigningCertificate( + packageName, certificate, PackageManager.CERT_INPUT_SHA256)) { + Log.d(TAG, "Package " + packageName + " has expected signature"); + hasMatchingCert = true; + } + } catch (IllegalArgumentException e) { + Log.w(TAG, "Failed to parse signing certificate: " + certHash, e); + throw new IllegalStateException( + "Failed to parse signing certificate: " + certHash, e); + } + } + + if (!hasMatchingCert) { + Log.e( + TAG, + "Package " + + packageName + + " for source " + + safetySourceId + + " signed with invalid signature"); + throw new IllegalArgumentException("Invalid signature for package " + packageName); + } + } + private boolean processSafetyEvent( @NonNull String safetySourceId, @NonNull SafetyEvent safetyEvent, diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java index 2e2d56066..8d6449f92 100644 --- a/service/java/com/android/safetycenter/SafetyCenterService.java +++ b/service/java/com/android/safetycenter/SafetyCenterService.java @@ -19,8 +19,10 @@ package com.android.safetycenter; import static android.Manifest.permission.MANAGE_SAFETY_CENTER; import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS; import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER; import static android.safetycenter.SafetyCenterManager.RefreshReason; import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED; @@ -71,6 +73,7 @@ import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; import com.android.modules.utils.BackgroundThread; +import com.android.modules.utils.build.SdkLevel; import com.android.permission.util.ForegroundThread; import com.android.permission.util.UserUtils; import com.android.safetycenter.internaldata.SafetyCenterIds; @@ -114,11 +117,6 @@ public final class SafetyCenterService extends SystemService { /** The name of the file used to persist the Safety Center issue cache. */ private static final String SAFETY_CENTER_ISSUES_CACHE_FILE_NAME = "safety_center_issues.xml"; - /** The START_TASKS_FROM_RECENTS permission. */ - // TODO(b/242905922): Remove once in API. - private static final String START_TASKS_FROM_RECENTS = - "android.permission.START_TASKS_FROM_RECENTS"; - /** The time delay used to throttle and aggregate writes to disk. */ private static final Duration WRITE_DELAY = Duration.ofMillis(500); @@ -199,14 +197,18 @@ public final class SafetyCenterService extends SystemService { mSafetyCenterRefreshTracker, mPendingIntentFactory, mSafetyCenterIssueCache, - mSafetyCenterRepository); + mSafetyCenterRepository, + SdkLevel.isAtLeastU() + ? new SafetyCenterIssueDeduplicator(mSafetyCenterIssueCache) + : null); mSafetyCenterListeners = new SafetyCenterListeners(mSafetyCenterDataFactory); mNotificationSender = new SafetyCenterNotificationSender( context, new SafetyCenterNotificationFactory(context), mSafetyCenterIssueCache, - mSafetyCenterRepository); + mSafetyCenterRepository, + mSafetyCenterConfigReader); mSafetyCenterBroadcastDispatcher = new SafetyCenterBroadcastDispatcher( context, mSafetyCenterConfigReader, mSafetyCenterRefreshTracker); @@ -386,6 +388,23 @@ public final class SafetyCenterService extends SystemService { } @Override + @RequiresApi(UPSIDE_DOWN_CAKE) + public void refreshSpecificSafetySources( + @RefreshReason int refreshReason, + @UserIdInt int userId, + @NonNull List<String> safetySourceIds) { + getContext() + .enforceCallingPermission(MANAGE_SAFETY_CENTER, "refreshSpecificSafetySources"); + requireNonNull(safetySourceIds, "safetySourceIds cannot be null"); + RefreshReasons.validate(refreshReason); + if (!enforceCrossUserPermission("refreshSpecificSafetySources", userId) + || !checkApiEnabled("refreshSpecificSafetySources")) { + return; + } + startRefreshingSafetySources(refreshReason, userId, safetySourceIds); + } + + @Override @Nullable public SafetyCenterConfig getSafetyCenterConfig() { getContext() @@ -966,13 +985,20 @@ public final class SafetyCenterService extends SystemService { private void startRefreshingSafetySources( @RefreshReason int refreshReason, @UserIdInt int userId) { + startRefreshingSafetySources(refreshReason, userId, null); + } + + private void startRefreshingSafetySources( + @RefreshReason int refreshReason, + @UserIdInt int userId, + @Nullable List<String> safetySourceIds) { UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId); synchronized (mApiLock) { mSafetyCenterRepository.clearSafetySourceErrors(userProfileGroup); String refreshBroadcastId = mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources( - refreshReason, userProfileGroup); + refreshReason, userProfileGroup, safetySourceIds); if (refreshBroadcastId == null) { return; } diff --git a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java index d0f972a08..7083d71da 100644 --- a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java +++ b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java @@ -21,6 +21,7 @@ import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOC import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN; +import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK; import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED; @@ -36,6 +37,7 @@ import android.safetycenter.SafetyCenterManager.RefreshReason; import androidx.annotation.RequiresApi; import com.android.modules.utils.BasicShellCommandHandler; +import com.android.modules.utils.build.SdkLevel; import java.io.PrintWriter; import java.util.LinkedHashMap; @@ -176,6 +178,9 @@ final class SafetyCenterShellCommandHandler extends BasicShellCommandHandler { reasons.put("LOCALE_CHANGE", REFRESH_REASON_DEVICE_LOCALE_CHANGE); reasons.put("SAFETY_CENTER_ENABLED", REFRESH_REASON_SAFETY_CENTER_ENABLED); reasons.put("OTHER", REFRESH_REASON_OTHER); + if (SdkLevel.isAtLeastU()) { + reasons.put("PERIODIC", REFRESH_REASON_PERIODIC); + } return unmodifiableMap(reasons); } } diff --git a/service/java/com/android/safetycenter/TEST_MAPPING b/service/java/com/android/safetycenter/TEST_MAPPING index e2eb5ef5d..c702ee852 100644 --- a/service/java/com/android/safetycenter/TEST_MAPPING +++ b/service/java/com/android/safetycenter/TEST_MAPPING @@ -24,5 +24,15 @@ { "name": "SafetyCenterFunctionalTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/tests/cts/safetycenter/Android.bp b/tests/cts/safetycenter/Android.bp index 5c85ea3a9..b7519201f 100644 --- a/tests/cts/safetycenter/Android.bp +++ b/tests/cts/safetycenter/Android.bp @@ -43,6 +43,7 @@ android_test { "safety-center-config", "safety-center-internal-data", "safety-center-resources-lib", + "modules-utils-build", "truth-prebuilt", "Nene", "Harrier", diff --git a/tests/cts/safetycenter/AndroidTest.xml b/tests/cts/safetycenter/AndroidTest.xml index f169f96f7..9f512231a 100644 --- a/tests/cts/safetycenter/AndroidTest.xml +++ b/tests/cts/safetycenter/AndroidTest.xml @@ -28,6 +28,7 @@ <!-- Multi-user code is tested separately using Bedstead. See SafetyCenterMultiUsersTest. --> <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user"/> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" /> <option name="test-suite-tag" value="cts"/> diff --git a/tests/cts/safetycenter/TEST_MAPPING b/tests/cts/safetycenter/TEST_MAPPING index 5a26efe66..5feb2e901 100644 --- a/tests/cts/safetycenter/TEST_MAPPING +++ b/tests/cts/safetycenter/TEST_MAPPING @@ -13,5 +13,15 @@ { "name": "CtsSafetyCenterTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt index 88bcf6554..74d20d706 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt @@ -19,6 +19,7 @@ package android.safetycenter.cts import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.os.Build import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntry import android.safetycenter.SafetyCenterEntryGroup @@ -27,9 +28,11 @@ import android.safetycenter.SafetyCenterIssue import android.safetycenter.SafetyCenterStaticEntry import android.safetycenter.SafetyCenterStaticEntryGroup import android.safetycenter.SafetyCenterStatus +import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withDismissedIssuesIfAtLeastU import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith @@ -116,6 +119,23 @@ class SafetyCenterDataTest { } @Test + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getDismissedIssues_returnsDismissedIssues() { + val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2)) + + assertThat(data1.dismissedIssues).isEmpty() + assertThat(data3.dismissedIssues).containsExactly(issue2) + } + + @Test + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getDismissedIssues_mutationsAreNotAllowed() { + val mutatedDismissedIssues = data1.dismissedIssues + + assertFailsWith(UnsupportedOperationException::class) { mutatedDismissedIssues.add(issue2) } + } + + @Test fun getIssues_mutationsAreNotAllowed() { val mutatedIssues = data1.issues @@ -165,31 +185,95 @@ class SafetyCenterDataTest { } @Test + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun parcelRoundTrip_withDismissedIssues_recreatesEqual() { + val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2)) + val data4 = data2.withDismissedIssuesIfAtLeastU(listOf(issue1)) + + assertThat(data3).recreatesEqual(SafetyCenterData.CREATOR) + assertThat(data4).recreatesEqual(SafetyCenterData.CREATOR) + } + + @Test fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { + val equalsHashCodeToStringTester = + EqualsHashCodeToStringTester() + .addEqualityGroup( + data1, + SafetyCenterData( + status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))) + .addEqualityGroup( + data2, + SafetyCenterData( + status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2))) + .addEqualityGroup( + SafetyCenterData(status1, listOf(), listOf(), listOf()), + SafetyCenterData(status1, listOf(), listOf(), listOf())) + .addEqualityGroup( + SafetyCenterData( + status2, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))) + .addEqualityGroup( + SafetyCenterData( + status1, listOf(issue2), listOf(entryOrGroup1), listOf(staticEntryGroup1))) + .addEqualityGroup( + SafetyCenterData( + status1, listOf(issue1), listOf(entryOrGroup2), listOf(staticEntryGroup1))) + .addEqualityGroup( + SafetyCenterData( + status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup2))) + + equalsHashCodeToStringTester.test() + } + + @Test + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun equalsHashCodeToString_withDismissedIssues_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester() .addEqualityGroup( data1, SafetyCenterData( - status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))) - .addEqualityGroup( - data2, + status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)), SafetyCenterData( - status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2))) + status1, + listOf(issue1), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf())) .addEqualityGroup( - SafetyCenterData(status1, listOf(), listOf(), listOf()), - SafetyCenterData(status1, listOf(), listOf(), listOf())) + SafetyCenterData( + status1, + listOf(), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf(issue1))) .addEqualityGroup( SafetyCenterData( - status2, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))) + status1, + listOf(), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf(issue2))) .addEqualityGroup( SafetyCenterData( - status1, listOf(issue2), listOf(entryOrGroup1), listOf(staticEntryGroup1))) + status1, + listOf(issue2), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf(issue1))) .addEqualityGroup( SafetyCenterData( - status1, listOf(issue1), listOf(entryOrGroup2), listOf(staticEntryGroup1))) + status1, + listOf(issue1), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf(issue2))) .addEqualityGroup( SafetyCenterData( - status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup2))) + status1, + listOf(), + listOf(entryOrGroup1), + listOf(staticEntryGroup1), + listOf(issue1, issue2))) .test() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt index c57b6481d..f97278c22 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt @@ -19,13 +19,19 @@ package android.safetycenter.cts import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.os.Build +import android.os.Build.VERSION_CODES.TIRAMISU +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetyCenterIssue +import androidx.annotation.RequiresApi import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith +import org.junit.Assume.assumeFalse import org.junit.Test import org.junit.runner.RunWith @@ -99,6 +105,33 @@ class SafetyCenterIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getAttributionTitle_returnsAttributionTitle() { + assertThat( + SafetyCenterIssue.Builder(issue1) + .setAttributionTitle("an attributionTitle") + .build() + .attributionTitle) + .isEqualTo("an attributionTitle") + assertThat( + SafetyCenterIssue.Builder(issue1) + .setAttributionTitle("another attributionTitle") + .build() + .attributionTitle) + .isEqualTo("another attributionTitle") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getAttributionTitle_withNullAttributionTitle_returnsNull() { + val safetyCenterIssue = + SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this") + .build() + + assertThat(safetyCenterIssue.attributionTitle).isNull() + } + + @Test fun getSeverityLevel_returnsSeverityLevel() { assertThat( SafetyCenterIssue.Builder(issue1) @@ -182,6 +215,64 @@ class SafetyCenterIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getGroupId_withNonNullValue_returnsGroupId() { + val issue = SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build() + + assertThat(issue.groupId).isEqualTo("group_id") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getGroupId_withNullValue_returnsNull() { + val issue = + SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this") + .build() + + assertThat(issue.groupId).isNull() + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getGroupId_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val issue = + SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this") + .build() + + val exception = assertFailsWith(UnsupportedOperationException::class) { issue.groupId } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setGroupId_withNullValue_returnsNull() { + val issue = SafetyCenterIssue.Builder(issue1).setGroupId(null).build() + + assertThat(issue.groupId).isNull() + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setGroupId_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + + val exception = + assertFailsWith(UnsupportedOperationException::class) { + SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build() + } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE") + } + + @Test fun describeContents_returns0() { assertThat(issue1.describeContents()).isEqualTo(0) assertThat(issueWithRequiredFieldsOnly.describeContents()).isEqualTo(0) @@ -194,37 +285,31 @@ class SafetyCenterIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun parcelRoundTrip_recreatesEqual_atLeastAndroidU() { + val safetyCenterIssue = + SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this") + .setSubtitle("In the neighborhood") + .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) + .setDismissible(true) + .setShouldConfirmDismissal(true) + .setActions(listOf(action1)) + .setAttributionTitle("Attribution title") + .setGroupId("group_id") + .build() + + assertThat(safetyCenterIssue).recreatesEqual(SafetyCenterIssue.CREATOR) + } + + @Test fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { - EqualsHashCodeToStringTester() - .addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build()) - .addEqualityGroup(issueWithRequiredFieldsOnly) - .addEqualityGroup( - SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this") - .setSubtitle("In the neighborhood") - .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) - .setActions(listOf(action1)) - .build(), - SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this") - .setSubtitle("In the neighborhood") - .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) - .setActions(listOf(action1)) - .build()) - .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build()) - .addEqualityGroup( - SafetyCenterIssue.Builder(issue1).setTitle("a different title").build()) - .addEqualityGroup( - SafetyCenterIssue.Builder(issue1).setSubtitle("a different subtitle").build()) - .addEqualityGroup( - SafetyCenterIssue.Builder(issue1).setSummary("a different summary").build()) - .addEqualityGroup( - SafetyCenterIssue.Builder(issue1) - .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING) - .build()) - .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setDismissible(false).build()) - .addEqualityGroup( - SafetyCenterIssue.Builder(issue1).setShouldConfirmDismissal(false).build()) - .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setActions(listOf(action2)).build()) - .test() + newTiramisuEqualsHashCodeToStringTester().test() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() { + newUpsideDownCakeEqualsHashCodeToStringTester().test() } @Test @@ -329,4 +414,80 @@ class SafetyCenterIssueTest { .build()) .test() } + + /** + * Creates a new [EqualsHashCodeToStringTester] instance with all the equality groups in the + * [newTiramisuEqualsHashCodeToStringTester] plus new equality groups covering all the new + * fields added in U. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + private fun newUpsideDownCakeEqualsHashCodeToStringTester(): EqualsHashCodeToStringTester { + val issueWithTiramisuFields = + SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this") + .setSubtitle("In the neighborhood") + .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) + .setDismissible(true) + .setShouldConfirmDismissal(true) + .setActions(listOf(action1)) + .build() + return newTiramisuEqualsHashCodeToStringTester() + .addEqualityGroup( + SafetyCenterIssue.Builder(issueWithTiramisuFields) + .setAttributionTitle("Attribution title") + .build(), + SafetyCenterIssue.Builder(issueWithTiramisuFields) + .setAttributionTitle("Attribution title") + .build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issueWithTiramisuFields) + .setAttributionTitle("a different attribution title") + .build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issueWithTiramisuFields) + .setAttributionTitle("Attribution title") + .setGroupId("group_id") + .build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(), + SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issueWithTiramisuFields) + .setGroupId("a different group_id") + .build()) + } + + /** + * Creates a new [EqualsHashCodeToStringTester] instance which covers all the fields in the T + * API and is safe to use on any T+ API level. + */ + private fun newTiramisuEqualsHashCodeToStringTester() = + EqualsHashCodeToStringTester() + .addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build()) + .addEqualityGroup(issueWithRequiredFieldsOnly) + .addEqualityGroup( + SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this") + .setSubtitle("In the neighborhood") + .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) + .setActions(listOf(action1)) + .build(), + SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this") + .setSubtitle("In the neighborhood") + .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) + .setActions(listOf(action1)) + .build()) + .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issue1).setTitle("a different title").build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issue1).setSubtitle("a different subtitle").build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issue1).setSummary("a different summary").build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issue1) + .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING) + .build()) + .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setDismissible(false).build()) + .addEqualityGroup( + SafetyCenterIssue.Builder(issue1).setShouldConfirmDismissal(false).build()) + .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setActions(listOf(action2)).build()) } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt index 3c0ed52a3..50d192ccc 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt @@ -17,6 +17,10 @@ package android.safetycenter.cts import android.content.Context +import android.os.Build +import android.os.Build.VERSION.CODENAME +import android.os.Build.VERSION_CODES.TIRAMISU +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.UserHandle.USER_NULL import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntry @@ -35,6 +39,7 @@ import android.safetycenter.SafetyCenterManager import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener import android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN +import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK import android.safetycenter.SafetyCenterStaticEntry import android.safetycenter.SafetyCenterStaticEntryGroup @@ -83,21 +88,26 @@ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_DISABLED_ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_GROUP_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_WITH_SEARCH_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_COLLAPSIBLE_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_RIGID_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATEFUL_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATELESS_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_OTHER_PACKAGE_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_BAREBONE_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_RIGID_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MIXED_COLLAPSIBLE_GROUP_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_STATELESS_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_CONFIG +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MIXED_STATEFUL_GROUP_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.NO_PAGE_OPEN_CONFIG +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.PACKAGE_CERT_HASH_INVALID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SAMPLE_SOURCE_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SEVERITY_ZERO_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_INVALID_INTENT_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_OTHER_PACKAGE_CONFIG +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_WITH_FAKE_CERT +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_WITH_INVALID_CERT import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_1 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3 @@ -106,11 +116,14 @@ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_5 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_6 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_7 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_BAREBONE_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_IN_COLLAPSIBLE_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_IN_STATEFUL_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SUMMARY_TEST_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SUMMARY_TEST_GROUP_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.getLockScreenSourceConfig +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.multipleSourcesWithDeduplicationInfoConfig import android.safetycenter.cts.testing.SafetyCenterCtsData +import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withAttributionTitleInIssuesIfAtLeastU +import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withDismissedIssuesIfAtLeastU import android.safetycenter.cts.testing.SafetyCenterCtsHelper import android.safetycenter.cts.testing.SafetyCenterCtsListener import android.safetycenter.cts.testing.SafetyCenterEnabledChangedReceiver @@ -131,6 +144,7 @@ import android.safetycenter.cts.testing.SafetySourceReceiver.Companion.refreshSa import android.safetycenter.cts.testing.SafetySourceReceiver.Companion.refreshSafetySourcesWithoutReceiverPermissionAndWait import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress import com.android.compatibility.common.preconditions.ScreenLockHelper import com.android.safetycenter.resources.SafetyCenterResourcesContext import com.google.common.base.Preconditions.checkState @@ -140,6 +154,7 @@ import java.time.Duration import kotlin.test.assertFailsWith import kotlinx.coroutines.TimeoutCancellationException import org.junit.After +import org.junit.Assume.assumeFalse import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test @@ -275,14 +290,14 @@ class SafetyCenterManagerTest { private val safetyCenterEntryGroupMixedFromComplexConfig = SafetyCenterEntryOrGroup( - SafetyCenterEntryGroup.Builder(MIXED_COLLAPSIBLE_GROUP_ID, "OK") + SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) .setSummary(safetyCenterResourcesContext.getStringByName("group_unknown_summary")) .setEntries( listOf( - safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_IN_COLLAPSIBLE_ID), + safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID), SafetyCenterEntry.Builder( - SafetyCenterCtsData.entryId(STATIC_IN_COLLAPSIBLE_ID), "OK") + SafetyCenterCtsData.entryId(STATIC_IN_STATEFUL_ID), "OK") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setSummary("OK") .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent) @@ -516,8 +531,8 @@ class SafetyCenterManagerTest { safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID), safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID), safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID), - safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID), - safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID)), + safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID), + safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID)), listOf( SafetyCenterEntryOrGroup( SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") @@ -632,14 +647,14 @@ class SafetyCenterManagerTest { } @Test - fun setSafetySourceData_sourceInRigidGroupUnspecified_setsValue() { + fun setSafetySourceData_sourceInStatelessGroupUnspecified_setsValue() { safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG) val dataToSet = safetySourceCtsData.unspecified - safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, dataToSet) + safetyCenterCtsHelper.setData(DYNAMIC_IN_STATELESS_ID, dataToSet) val apiSafetySourceData = - safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_IN_RIGID_ID) + safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_IN_STATELESS_ID) assertThat(apiSafetySourceData).isEqualTo(dataToSet) } @@ -685,19 +700,50 @@ class SafetyCenterManagerTest { } @Test - fun setSafetySourceData_sourceInRigidGroupNotUnspecified_throwsIllegalArgumentException() { + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setSafetySourceData_wronglySignedPackage_throwsIllegalArgumentException() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_WITH_FAKE_CERT) + + val thrown = + assertFailsWith(IllegalArgumentException::class) { + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified) + } + + assertThat(thrown) + .hasMessageThat() + .isEqualTo("Invalid signature for package " + context.packageName) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setSafetySourceData_invalidPackageCertificate_throwsIllegalArgumentException() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_WITH_INVALID_CERT) + + val thrown = + assertFailsWith(IllegalStateException::class) { + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified) + } + + assertThat(thrown) + .hasMessageThat() + .isEqualTo("Failed to parse signing certificate: " + PACKAGE_CERT_HASH_INVALID) + } + + @Test + fun setSafetySourceData_sourceInStatelessGroupNotUnspecified_throwsIllegalArgumentException() { safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG) val thrown = assertFailsWith(IllegalArgumentException::class) { - safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.information) + safetyCenterCtsHelper.setData( + DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.information) } assertThat(thrown) .hasMessageThat() .isEqualTo( - "Safety source: $DYNAMIC_IN_RIGID_ID is in a rigid group but specified a severity" + - " level: ${SafetySourceData.SEVERITY_LEVEL_INFORMATION}") + "Safety source: $DYNAMIC_IN_STATELESS_ID is in a stateless group but specified a " + + "severity level: $SEVERITY_LEVEL_INFORMATION") } @Test @@ -1281,6 +1327,21 @@ class SafetyCenterManagerTest { } @Test + fun refreshSafetySources_withRefreshReasonPageOpen_calledIfSourceHasADeviceConfigOverride() { + SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID) + safetyCenterCtsHelper.setConfig(NO_PAGE_OPEN_CONFIG) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceCtsData.information)) + + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN) + + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceCtsData.information) + } + + @Test fun refreshSafetySources_whenSourceClearsData_sourceSendsNullData() { safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information) @@ -1779,6 +1840,109 @@ class SafetyCenterManagerTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun refreshSafetySources_withRefreshReasonPeriodic_noBackgroundRefreshSourceDoesNotSendData() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceCtsData.information)) + SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID) + + assertFailsWith(TimeoutCancellationException::class) { + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PERIODIC, TIMEOUT_SHORT) + } + + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(apiSafetySourceData).isNull() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun refreshSafetySources_withRefreshReasonPeriodic_backgroundRefreshSourceSendsData() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), + Response.SetData(safetySourceCtsData.criticalWithResolvingGeneralIssue)) + + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PERIODIC) + + val sourceData = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(sourceData).isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun refreshSafetySources_withSafetySourceIds_onlySpecifiedSourcesSendData() { + safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG) + SafetySourceReceiver.apply { + setResponse( + Request.Refresh(SOURCE_ID_1), Response.SetData(safetySourceCtsData.information)) + setResponse( + Request.Refresh(SOURCE_ID_2), Response.SetData(safetySourceCtsData.information)) + setResponse( + Request.Refresh(SOURCE_ID_3), Response.SetData(safetySourceCtsData.information)) + } + + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN, safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2)) + + val apiSafetySourceData1 = + safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_1) + assertThat(apiSafetySourceData1).isEqualTo(safetySourceCtsData.information) + val apiSafetySourceData2 = + safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_2) + assertThat(apiSafetySourceData2).isEqualTo(safetySourceCtsData.information) + val apiSafetySourceData3 = + safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_3) + assertThat(apiSafetySourceData3).isNull() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun refreshSafetySources_withEmptySafetySourceIds_NoSourcesSendData() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), + Response.SetData(safetySourceCtsData.criticalWithResolvingGeneralIssue)) + + assertFailsWith(TimeoutCancellationException::class) { + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN, TIMEOUT_SHORT, emptyList()) + } + + val sourceData = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(sourceData).isNull() + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun refreshSafetySources_versionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG) + + val exception = + assertFailsWith(UnsupportedOperationException::class) { + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN, safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3)) + } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun refreshSafetySources_withSafetySourceIds_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { + safetyCenterManager.refreshSafetySources(REFRESH_REASON_PAGE_OPEN, listOf()) + } + } + + @Test fun refreshSafetySources_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK) @@ -1846,11 +2010,71 @@ class SafetyCenterManagerTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_attributionTitleProvidedBySource_returnsIssueWithAttributionTitle() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssueWithAttributionTitle) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedSafetyCenterData = + safetyCenterDataOkOneAlert.withAttributionTitleInIssuesIfAtLeastU("Attribution Title") + assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_attributionTitleNotProvided_returnsGroupTitleAsAttributionTitle() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssue) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedSafetyCenterData = + safetyCenterDataOkOneAlert.withAttributionTitleInIssuesIfAtLeastU("OK") + assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_attributionNotSetAndGroupTitleNull_returnsNullAttributionTitle() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedSafetyCenterData = + SafetyCenterData( + safetyCenterStatusGeneralRecommendationOneAlert, + listOf( + safetyCenterCtsData.safetyCenterIssueRecommendation( + ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null)), + emptyList(), + emptyList()) + assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData) + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getSafetyCenterData_attributionNotSetBySourceOnTiramisu_returnsNullAttributionTitle() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(CODENAME == "UpsideDownCake") + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssue) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataOkOneAlert) + } + + @Test fun getSafetyCenterData_withUpdatedData_returnsUpdatedData() { safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information) - val previousApiSafetyCenterData = - safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + val previousApiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() safetyCenterCtsHelper.setData( SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue) @@ -1948,6 +2172,132 @@ class SafetyCenterManagerTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withRecommendationDataIssue_returnsDataRecommendationStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultRecommendationIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_data_recommendation_title", + OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withCriticalDataIssue_returnsDataCriticalStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultCriticalResolvingIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_critical_data_warning_title", + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withRecommendationPasswordsIssue_returnsDataRecommendationStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultRecommendationIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_passwords_recommendation_title", + OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withCriticalPasswordsIssue_returnsDataCriticalStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultCriticalResolvingIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_critical_passwords_warning_title", + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withRecommendationPersonalIssue_returnsDataRecommendationStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultRecommendationIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_personal_recommendation_title", + OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_withCriticalPersonalIssue_returnsDataCriticalStatus() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData + .defaultCriticalResolvingIssueBuilder() + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) + .build())) + + val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status + + assertThat(apiSafetyCenterStatus) + .isEqualTo( + safetyCenterCtsData.safetyCenterStatusOneAlert( + "overall_severity_level_critical_personal_warning_title", + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)) + } + + @Test fun getSafetyCenterData_singleSourceIssues_returnsOverallStatusBasedOnHigherSeverityIssue() { safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) safetyCenterCtsHelper.setData( @@ -1986,6 +2336,447 @@ class SafetyCenterManagerTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesOfSameSeverities_issueOfFirstSourceInConfigShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_differentDuplicationId_bothIssuesShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("different"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly( + safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1), + safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5)) + .inOrder() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_differentDuplicationGroup_bothIssuesShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_6, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly( + safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1), + safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_6)) + .inOrder() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_threeDuplicateIssues_onlyOneIssueShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_4, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_6, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_7, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_4)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesOfDifferentSeverities_moreSevereIssueShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_2, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_multipleDuplicationsOfIssues_correctlyDeduplicated() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("A"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_2, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("A"))) + // Belongs to DEDUPLICATION_GROUP_2 + safetyCenterCtsHelper.setData( + SOURCE_ID_3, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("B"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_4, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("B"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("A"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_6, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("B"))) + // Belongs to DEDUPLICATION_GROUP_3 + safetyCenterCtsHelper.setData( + SOURCE_ID_7, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("B"))) + + val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues + + assertThat(apiSafetyCenterIssues) + .containsExactly( + safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5), + safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_3), + safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_4)) + .inOrder() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesBothDismissed_topOneShownAsDismissed() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID)) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterIssues = apiSafetyCenterData.issues + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterIssues).isEmpty() + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesLowerSeverityOneDismissed_topOneShown() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID)) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterIssues = apiSafetyCenterData.issues + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + assertThat(apiSafetyCenterDismissedIssues).isEmpty() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesHigherSeverityOneDismissed_topOneShownAsDismissed() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterIssues = apiSafetyCenterData.issues + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterIssues).isEmpty() + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_dupIssuesLowerPrioritySameSeverityOneDismissed_topShownAsDismissed() { + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_5, CRITICAL_ISSUE_ID)) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterIssues = apiSafetyCenterData.issues + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterIssues).isEmpty() + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_dupIssuesTopOneDismissedThenDisappears_bottomOneReemergesTimely() { + SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to 99L) + SafetyCenterFlags.resurfaceIssueDelays = + mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY) + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + safetyCenterManager.getSafetyCenterDataWithPermission() // data used, 2nd issue dismissed + safetyCenterCtsHelper.setData(SOURCE_ID_1, SafetySourceCtsData.issuesOnly()) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5)) + waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { + val hasResurfaced = + safetyCenterManager + .getSafetyCenterDataWithPermission() + .issues + .contains(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5)) + hasResurfaced + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_dupsOfDiffSeveritiesTopOneDismissedThenGone_bottomOneReemergesTimely() { + SafetyCenterFlags.resurfaceIssueMaxCounts = + mapOf( + SEVERITY_LEVEL_INFORMATION to 0L, + SEVERITY_LEVEL_RECOMMENDATION to 99L, + SEVERITY_LEVEL_CRITICAL_WARNING to 0L) + SafetyCenterFlags.resurfaceIssueDelays = + mapOf( + SEVERITY_LEVEL_INFORMATION to Duration.ZERO, + SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY, + SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO) + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + safetyCenterManager.getSafetyCenterDataWithPermission() // data used, 2nd issue dismissed + safetyCenterCtsHelper.setData(SOURCE_ID_1, SafetySourceCtsData.issuesOnly()) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5)) + waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { + val hasResurfaced = + safetyCenterManager + .getSafetyCenterDataWithPermission() + .issues + .contains(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5)) + hasResurfaced + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesLowerOneResurfaces_lowerOneStillFilteredOut() { + SafetyCenterFlags.resurfaceIssueMaxCounts = + mapOf( + SEVERITY_LEVEL_INFORMATION to 0L, + SEVERITY_LEVEL_RECOMMENDATION to 99L, + SEVERITY_LEVEL_CRITICAL_WARNING to 99L) + SafetyCenterFlags.resurfaceIssueDelays = + mapOf( + SEVERITY_LEVEL_INFORMATION to Duration.ZERO, + SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY, + SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY.multipliedBy(100)) + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + + // data used, 2nd issue dismissed + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + assertFailsWith(TimeoutCancellationException::class) { + waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { + val hasResurfaced = + safetyCenterManager + .getSafetyCenterDataWithPermission() + .issues + .contains(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5)) + hasResurfaced + } + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getSafetyCenterData_duplicateIssuesTopOneResurfaces_topOneShown() { + SafetyCenterFlags.resurfaceIssueMaxCounts = + mapOf( + SEVERITY_LEVEL_INFORMATION to 0L, + SEVERITY_LEVEL_RECOMMENDATION to 99L, + SEVERITY_LEVEL_CRITICAL_WARNING to 99L) + SafetyCenterFlags.resurfaceIssueDelays = + mapOf( + SEVERITY_LEVEL_INFORMATION to Duration.ZERO, + SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY.multipliedBy(100), + SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY) + safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_1, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.criticalIssueWithDeduplicationId("same"))) + // Belongs to DEDUPLICATION_GROUP_1 + safetyCenterCtsHelper.setData( + SOURCE_ID_5, + SafetySourceCtsData.issuesOnly( + safetySourceCtsData.recommendationIssueWithDeduplicationId("same"))) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID)) + + // data used, 2nd issue dismissed + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues + + assertThat(apiSafetyCenterDismissedIssues) + .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { + val hasResurfaced = + safetyCenterManager + .getSafetyCenterDataWithPermission() + .issues + .contains(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1)) + hasResurfaced + } + } + + @Test fun getSafetyCenterData_criticalDeviceIssues_returnsOverallStatusBasedOnAddIssueCallOrder() { safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) safetyCenterCtsHelper.setData( @@ -2025,9 +2816,10 @@ class SafetyCenterManagerTest { safetyCenterCtsHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)) - safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssue) safetyCenterCtsHelper.setData( - ISSUE_ONLY_IN_RIGID_ID, + DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssue) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_IN_STATELESS_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue)) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -2067,7 +2859,7 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING, entrySummary = "critical 2")) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2105,7 +2897,7 @@ class SafetyCenterManagerTest { safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_RECOMMENDATION, entrySummary = "recommendation 2")) // SOURCE_ID_7 leave as an UNKNOWN dynamic entry - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2151,7 +2943,7 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 3")) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2194,7 +2986,7 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 4")) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2236,7 +3028,7 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 7")) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2268,7 +3060,7 @@ class SafetyCenterManagerTest { safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information")) // SOURCE_ID_7 leave as an UNKNOWN dynamic entry - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2299,7 +3091,7 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceCtsData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information")) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2334,7 +3126,7 @@ class SafetyCenterManagerTest { severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified with issue", withIssue = true)) - // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry + // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry val group = safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) @@ -2642,7 +3434,10 @@ class SafetyCenterManagerTest { SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)) val safetyCenterDataFromListener = listener.receiveSafetyCenterData() - assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataOkReviewCriticalEntry) + val expectedSafetyCenterData = + safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID))) + assertThat(safetyCenterDataFromListener).isEqualTo(expectedSafetyCenterData) } @Test @@ -2676,13 +3471,15 @@ class SafetyCenterManagerTest { safetyCenterCtsHelper.setData( SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue) val listener = safetyCenterCtsHelper.addListener() - safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, issueTypeId = "some_other_issue_type_id")) val safetyCenterDataFromListener = listener.receiveSafetyCenterData() - assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataOkReviewCriticalEntry) + val expectedSafetyCenterData = + safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID))) + assertThat(safetyCenterDataFromListener).isEqualTo(expectedSafetyCenterData) } @Test @@ -2699,8 +3496,10 @@ class SafetyCenterManagerTest { SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID)) val safetyCenterDataAfterDismissal = listener.receiveSafetyCenterData() - assertThat(safetyCenterDataAfterDismissal) - .isEqualTo(safetyCenterDataOkReviewRecommendationEntry) + val expectedSafetyCenterData = + safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID))) + assertThat(safetyCenterDataAfterDismissal).isEqualTo(expectedSafetyCenterData) val safetyCenterDataAfterSourceHandledDismissal = listener.receiveSafetyCenterData() assertThat(safetyCenterDataAfterSourceHandledDismissal).isEqualTo(safetyCenterDataOk) } @@ -2713,13 +3512,14 @@ class SafetyCenterManagerTest { safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, recommendationDismissPendingIntentIssue) recommendationDismissPendingIntentIssue.issues.first().onDismissPendingIntent!!.cancel() val listener = safetyCenterCtsHelper.addListener() - safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID)) val safetyCenterDataFromListener = listener.receiveSafetyCenterData() - assertThat(safetyCenterDataFromListener) - .isEqualTo(safetyCenterDataOkReviewRecommendationEntry) + val exectedSafetyCenterData = + safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID))) + assertThat(safetyCenterDataFromListener).isEqualTo(exectedSafetyCenterData) assertFailsWith(TimeoutCancellationException::class) { listener.receiveSafetyCenterErrorDetails(TIMEOUT_SHORT) } @@ -2787,10 +3587,14 @@ class SafetyCenterManagerTest { safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID)) + val expectedSafetyCenterData = + safetyCenterDataOk.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueInformation(SINGLE_SOURCE_ID))) assertFailsWith(TimeoutCancellationException::class) { waitForWithTimeout(timeout = TIMEOUT_SHORT) { val hasResurfaced = - safetyCenterManager.getSafetyCenterDataWithPermission() != safetyCenterDataOk + safetyCenterManager.getSafetyCenterDataWithPermission() != + expectedSafetyCenterData hasResurfaced } } @@ -2816,10 +3620,14 @@ class SafetyCenterManagerTest { safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID)) + val expectedSafetyCenterData = + safetyCenterDataOk.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueInformation(SINGLE_SOURCE_ID))) assertFailsWith(TimeoutCancellationException::class) { waitForWithTimeout(timeout = TIMEOUT_SHORT) { val hasResurfaced = - safetyCenterManager.getSafetyCenterDataWithPermission() != safetyCenterDataOk + safetyCenterManager.getSafetyCenterDataWithPermission() != + expectedSafetyCenterData hasResurfaced } } @@ -2855,11 +3663,15 @@ class SafetyCenterManagerTest { safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID)) + val expectedSafetyCenterData = + safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID))) assertFailsWith(TimeoutCancellationException::class) { waitForWithTimeout(timeout = TIMEOUT_SHORT) { val hasResurfaced = safetyCenterManager.getSafetyCenterDataWithPermission() != - safetyCenterDataOkReviewCriticalEntry + expectedSafetyCenterData + hasResurfaced } } @@ -2887,13 +3699,14 @@ class SafetyCenterManagerTest { val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() checkState(apiSafetyCenterData == safetyCenterDataDeviceRecommendationOneAlert) val listener = safetyCenterCtsHelper.addListener() - safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID)) val safetyCenterDataAfterDismissal = listener.receiveSafetyCenterData() - assertThat(safetyCenterDataAfterDismissal) - .isEqualTo(safetyCenterDataOkReviewRecommendationEntry) + val expectedSafetyCenterData = + safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU( + listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID))) + assertThat(safetyCenterDataAfterDismissal).isEqualTo(expectedSafetyCenterData) waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { val hasResurfacedExactly = safetyCenterManager.getSafetyCenterDataWithPermission() == diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt index 1876e3872..71367f659 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt @@ -43,11 +43,11 @@ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_BAREBONE_ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_DISABLED_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_GROUP_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_RIGID_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATELESS_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_PROFILE_SOURCE_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_BAREBONE_ID -import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_RIGID_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_STATELESS_ID import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_ALL_PROFILE_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ALL_PROFILE_CONFIG @@ -70,12 +70,12 @@ import android.safetycenter.cts.testing.UiTestHelper.waitAllTextNotDisplayed import androidx.test.core.app.ApplicationProvider import com.android.bedstead.harrier.BedsteadJUnit4 import com.android.bedstead.harrier.DeviceState -import com.android.bedstead.harrier.OptionalBoolean.TRUE import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile import com.android.bedstead.harrier.annotations.Postsubmit import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner +import com.android.bedstead.nene.types.OptionalBoolean.TRUE import com.android.compatibility.common.util.DisableAnimationRule import com.android.compatibility.common.util.FreezeRotationRule import com.android.safetycenter.resources.SafetyCenterResourcesContext @@ -124,11 +124,13 @@ class SafetyCenterMultiUsersTest { private val primaryProfileOnlyIssues = listOf( safetyCenterCtsData.safetyCenterIssueCritical(DYNAMIC_BAREBONE_ID), - safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID), + safetyCenterCtsData.safetyCenterIssueCritical( + ISSUE_ONLY_BAREBONE_ID, attributionTitle = null), safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID), - safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID), - safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID), - safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID)) + safetyCenterCtsData.safetyCenterIssueRecommendation( + ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null), + safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID), + safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID)) private val dynamicBareboneDefault = safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID) @@ -212,19 +214,19 @@ class SafetyCenterMultiUsersTest { .setEnabled(false) .build() - private val rigidEntry = + private val staticEntry = SafetyCenterStaticEntry.Builder("OK") .setSummary("OK") .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent) .build() - private val rigidEntryUpdated = + private val staticEntryUpdated = SafetyCenterStaticEntry.Builder("Unspecified title") .setSummary("Unspecified summary") .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent) .build() - private val rigidEntryForWorkBuilder + private val staticEntryForWorkBuilder get() = SafetyCenterStaticEntry.Builder("Paste") .setSummary("OK") @@ -232,18 +234,18 @@ class SafetyCenterMultiUsersTest { createTestActivityRedirectPendingIntentForUser( deviceState.workProfile().userHandle())) - private val rigidEntryForWork - get() = rigidEntryForWorkBuilder.build() + private val staticEntryForWork + get() = staticEntryForWorkBuilder.build() - private val rigidEntryForWorkPaused + private val staticEntryForWorkPaused get() = - rigidEntryForWorkBuilder + staticEntryForWorkBuilder // TODO(b/233188021): This needs to use the Enterprise API to override the "work" // keyword. .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused")) .build() - private val rigidEntryForWorkUpdated = + private val staticEntryForWorkUpdated = SafetyCenterStaticEntry.Builder("Unspecified title for Work") .setSummary("Unspecified summary") .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent) @@ -403,7 +405,7 @@ class SafetyCenterMultiUsersTest { staticGroupBuilder .setEntries(listOf(staticBarebone, staticAllOptional)) .build())), - listOf(SafetyCenterStaticEntryGroup("OK", listOf(rigidEntry, rigidEntry)))) + listOf(SafetyCenterStaticEntryGroup("OK", listOf(staticEntry, staticEntry)))) assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -440,7 +442,7 @@ class SafetyCenterMultiUsersTest { listOf( SafetyCenterStaticEntryGroup( "OK", - listOf(rigidEntry, rigidEntryForWork, rigidEntry, rigidEntryForWork)))) + listOf(staticEntry, staticEntryForWork, staticEntry, staticEntryForWork)))) assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -478,7 +480,10 @@ class SafetyCenterMultiUsersTest { SafetyCenterStaticEntryGroup( "OK", listOf( - rigidEntryUpdated, rigidEntryForWork, rigidEntry, rigidEntryForWork)))) + staticEntryUpdated, + staticEntryForWork, + staticEntry, + staticEntryForWork)))) assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -498,21 +503,23 @@ class SafetyCenterMultiUsersTest { safetyCenterCtsData.safetyCenterStatusCritical(11), listOf( safetyCenterCtsData.safetyCenterIssueCritical(DYNAMIC_BAREBONE_ID), - safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID), + safetyCenterCtsData.safetyCenterIssueCritical( + ISSUE_ONLY_BAREBONE_ID, attributionTitle = null), safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID), - safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID), + safetyCenterCtsData.safetyCenterIssueRecommendation( + ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null), safetyCenterCtsData.safetyCenterIssueInformation( DYNAMIC_DISABLED_ID, managedUserId), safetyCenterCtsData.safetyCenterIssueInformation( DYNAMIC_HIDDEN_ID, managedUserId), safetyCenterCtsData.safetyCenterIssueInformation( - ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId), - safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID), + ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId, attributionTitle = null), + safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID), safetyCenterCtsData.safetyCenterIssueInformation( - DYNAMIC_IN_RIGID_ID, managedUserId), - safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID), + DYNAMIC_IN_STATELESS_ID, managedUserId), + safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID), safetyCenterCtsData.safetyCenterIssueInformation( - ISSUE_ONLY_IN_RIGID_ID, managedUserId)), + ISSUE_ONLY_IN_STATELESS_ID, managedUserId)), listOf( SafetyCenterEntryOrGroup( SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") @@ -535,10 +542,10 @@ class SafetyCenterMultiUsersTest { SafetyCenterStaticEntryGroup( "OK", listOf( - rigidEntryUpdated, - rigidEntryForWorkUpdated, - rigidEntry, - rigidEntryForWork)))) + staticEntryUpdated, + staticEntryForWorkUpdated, + staticEntry, + staticEntryForWork)))) assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -583,10 +590,10 @@ class SafetyCenterMultiUsersTest { SafetyCenterStaticEntryGroup( "OK", listOf( - rigidEntryUpdated, - rigidEntryForWorkPaused, - rigidEntry, - rigidEntryForWorkPaused)))) + staticEntryUpdated, + staticEntryForWorkPaused, + staticEntry, + staticEntryForWorkPaused)))) assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -919,9 +926,10 @@ class SafetyCenterMultiUsersTest { safetyCenterCtsHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)) - safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssue) safetyCenterCtsHelper.setData( - ISSUE_ONLY_IN_RIGID_ID, + DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssue) + safetyCenterCtsHelper.setData( + ISSUE_ONLY_IN_STATELESS_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue)) } @@ -936,9 +944,9 @@ class SafetyCenterMultiUsersTest { ISSUE_ONLY_ALL_OPTIONAL_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue)) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( - DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssueForWork) + DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssueForWork) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( - ISSUE_ONLY_IN_RIGID_ID, + ISSUE_ONLY_IN_STATELESS_ID, SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue)) } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt index bcfa98840..853490064 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt @@ -17,7 +17,9 @@ package android.safetycenter.cts import android.content.Context +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetyCenterManager +import android.safetycenter.SafetySourceIssue import android.safetycenter.cts.testing.Coroutines import android.safetycenter.cts.testing.CtsNotificationListener import android.safetycenter.cts.testing.NotificationCharacteristics @@ -27,6 +29,8 @@ import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.cle import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.dynamicSafetySourceBuilder +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.singleSourceConfig import android.safetycenter.cts.testing.SafetyCenterCtsData import android.safetycenter.cts.testing.SafetyCenterCtsHelper import android.safetycenter.cts.testing.SafetyCenterFlags @@ -34,6 +38,7 @@ import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCe import android.safetycenter.cts.testing.SafetySourceCtsData import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress import com.android.compatibility.common.util.SystemUtil import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith @@ -113,7 +118,49 @@ class SafetyCenterNotificationTest { } @Test - fun setSafetySourceData_withNotificationAllowedSource_sendsNotification() { + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setSafetySourceData_withNotificationBehaviorNever_noNotification() { + val data = + safetySourceCtsData + .defaultRecommendationDataBuilder() + .addIssue( + safetySourceCtsData + .defaultRecommendationIssueBuilder() + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER) + .build()) + .build() + CtsNotificationListener.assertNoNotificationsPosted { + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setSafetySourceData_withNotificationBehaviorImmediately_sendsNotification() { + val data = + safetySourceCtsData + .defaultRecommendationDataBuilder() + .addIssue( + safetySourceCtsData + .defaultRecommendationIssueBuilder("Notify immediately", "This is urgent!") + .setNotificationBehavior( + SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY) + .build()) + .build() + + val notification = + CtsNotificationListener.getNextNotificationPostedOrNull { + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data) + } + + assertThat(notification).isNotNull() + assertNotificationMatches( + notification!!, + NotificationCharacteristics(title = "Notify immediately", text = "This is urgent!")) + } + + fun setSafetySourceData_withNotificationsAllowedForSourceByFlag_sendsNotification() { + SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ID) val data = safetySourceCtsData.recommendationWithAccountIssue val notification = @@ -129,6 +176,28 @@ class SafetyCenterNotificationTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setSafetySourceData_withNotificationsAllowedForSourceByConfig_sendsNotification() { + safetyCenterCtsHelper.setConfig( + singleSourceConfig( + dynamicSafetySourceBuilder("MyNotifiableSource") + .setNotificationsAllowed(true) + .build())) + val data = safetySourceCtsData.recommendationWithAccountIssue + + val notification = + CtsNotificationListener.getNextNotificationPostedOrNull { + safetyCenterCtsHelper.setData("MyNotifiableSource", data) + } + + assertThat(notification).isNotNull() + assertNotificationMatches( + notification!!, + NotificationCharacteristics( + title = "Recommendation issue title", text = "Recommendation issue summary")) + } + + @Test fun setSafetySourceData_twiceWithSameIssueId_updatesNotification() { val data1 = safetySourceCtsData diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt index 393aca714..2da9f2bee 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt @@ -20,6 +20,9 @@ import android.app.PendingIntent import android.app.PendingIntent.FLAG_IMMUTABLE import android.content.Context import android.content.Intent +import android.os.Build +import android.os.Build.VERSION_CODES.TIRAMISU +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED @@ -28,13 +31,18 @@ import android.safetycenter.SafetySourceIssue.Action import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_DEVICE import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_GENERAL +import android.safetycenter.SafetySourceIssue.Notification import android.safetycenter.cts.testing.Generic +import androidx.annotation.RequiresApi import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress +import com.android.modules.utils.build.SdkLevel import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith +import org.junit.Assume.assumeFalse import org.junit.Test import org.junit.runner.RunWith @@ -180,6 +188,190 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_getTitle_returnsTitle() { + val notification = Notification.Builder("Notification title", "Notification text").build() + + assertThat(notification.title).isEqualTo("Notification title") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_getText_returnsText() { + val notification = Notification.Builder("Notification title", "Notification text").build() + + assertThat(notification.text).isEqualTo("Notification text") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_getActions_withDefaultBuilder_returnsEmptyList() { + val notification = Notification.Builder("", "").build() + + assertThat(notification.actions).isEmpty() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_getActions_returnsActions() { + val notification = + Notification.Builder("", "").addAction(action1).addAction(action2).build() + + assertThat(notification.actions).containsExactly(action1, action2).inOrder() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_getActions_mutationsAreNotAllowed() { + val notification = + Notification.Builder("", "").addAction(action1).addAction(action2).build() + + assertFailsWith(UnsupportedOperationException::class) { notification.actions.add(action3) } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_describeContents_returns0() { + val notification = + Notification.Builder("Notification title", "Notification text") + .addAction(action1) + .addAction(action2) + .build() + + assertThat(notification.describeContents()).isEqualTo(0) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_parcelRoundTrip_recreatesEqual() { + val notification = + Notification.Builder("Notification title", "Notification text") + .addAction(action1) + .addAction(action2) + .build() + + assertThat(notification).recreatesEqual(Notification.CREATOR) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_withNullTitle_throwsNullPointerException() { + assertFailsWith(NullPointerException::class) { + Notification.Builder(Generic.asNull(), "Notification text") + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_withNullText_throwsNullPointerException() { + assertFailsWith(NullPointerException::class) { + Notification.Builder("Notification title", Generic.asNull()) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_addAction_doesNotMutatePreviouslyBuiltInstance() { + val notificationBuilder = Notification.Builder("", "").addAction(action1) + val actions = notificationBuilder.build().actions + + notificationBuilder.addAction(action2) + + assertThat(actions).containsExactly(action1) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_addAction_withNull_throwsIllegalArgumentException() { + assertFailsWith(NullPointerException::class) { + Notification.Builder("", "").addAction(Generic.asNull()) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_setActions_withNull_throwsIllegalArgumentException() { + assertFailsWith(NullPointerException::class) { + Notification.Builder("", "").setActions(Generic.asNull()) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_setActions_removesAllPreviouslyAddedActions() { + val notification = + Notification.Builder("", "") + .addAction(action1) + .addAction(action2) + .setActions(listOf(action3)) + .build() + + assertThat(notification.actions).containsExactly(action3) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_builder_clearActions_removesAllActions() { + val notification = + Notification.Builder("", "") + .addAction(action1) + .addAction(action2) + .clearActions() + .addAction(action3) + .build() + + assertThat(notification.actions).containsExactly(action3) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_build_withDuplicateActionIds_throwsIllegalArgumentException() { + val notificationBuilder = + Notification.Builder("Notification title", "Notification text") + .addAction(action1) + .addAction(action1) + + val exception = + assertFailsWith(IllegalArgumentException::class) { notificationBuilder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo("Custom notification cannot have duplicate action ids") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_build_withMoreThanTwoActions_throwsIllegalArgumentException() { + val notificationBuilder = + Notification.Builder("Notification title", "Notification text") + .addAction(action1) + .addAction(action2) + .addAction(action3) + + val exception = + assertFailsWith(IllegalArgumentException::class) { notificationBuilder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo("Custom notification must not contain more than 2 actions") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun notification_equalsHashCodeToString_usingEqualsHashCodeToStringTester() { + EqualsHashCodeToStringTester() + .addEqualityGroup( + Notification.Builder("Title", "Text").build(), + Notification.Builder("Title", "Text").build(), + ) + .addEqualityGroup(Notification.Builder("Other title", "Text").build()) + .addEqualityGroup(Notification.Builder("Title", "Other text").build()) + .addEqualityGroup(Notification.Builder("Title", "Text").addAction(action1).build()) + .addEqualityGroup(Notification.Builder("Title", "Text").addAction(action2).build()) + .addEqualityGroup( + Notification.Builder("Title", "Text").addAction(action1).addAction(action2).build()) + .test() + } + + @Test fun getId_returnsId() { val safetySourceIssue = SafetySourceIssue.Builder( @@ -256,6 +448,75 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getAttributionTitle_withNullAttributionTitle_returnsNull() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertThat(safetySourceIssue.attributionTitle).isNull() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getAttributionTitle_returnsAttributionTitle() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setAttributionTitle("attribution title") + .build() + + assertThat(safetySourceIssue.attributionTitle).isEqualTo("attribution title") + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.attributionTitle } + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssueBuilder.setAttributionTitle("title") + } + } + + @Test fun getSeverityLevel_returnsSeverityLevel() { val safetySourceIssue = SafetySourceIssue.Builder( @@ -302,6 +563,24 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getIssueCategory_whenSetExplicitlyWithUValueOnU_returnsIssueCategory() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) + .build() + + assertThat(safetySourceIssue.issueCategory) + .isEqualTo(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) + } + + @Test fun getActions_returnsActions() { val safetySourceIssue = SafetySourceIssue.Builder( @@ -401,6 +680,75 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getDeduplicationId_withDefaultBuilder_returnsNull() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertThat(safetySourceIssue.deduplicationId).isNull() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getDeduplicationId_whenSetExplicitly_returnsDeduplicationId() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setDeduplicationId("deduplication_id") + .build() + + assertThat(safetySourceIssue.deduplicationId).isEqualTo("deduplication_id") + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.deduplicationId } + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssueBuilder.setDeduplicationId("id") + } + } + + @Test fun getIssueTypeId_returnsIssueTypeId() { val safetySourceIssue = SafetySourceIssue.Builder( @@ -416,6 +764,268 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getCustomNotification_withDefaultBuilder_returnsNull() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertThat(safetySourceIssue.customNotification).isNull() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getCustomNotification_whenSetExplicitly_returnsCustomNotification() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setCustomNotification( + Notification.Builder("Notification title", "Notification text") + .addAction(action2) + .build()) + .build() + + assertThat(safetySourceIssue.customNotification) + .isEqualTo( + Notification.Builder("Notification title", "Notification text") + .addAction(action2) + .build()) + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssue.customNotification + } + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssueBuilder.setCustomNotification(null) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getNotificationBehavior_withDefaultBuilder_returnsUnspecified() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertThat(safetySourceIssue.notificationBehavior) + .isEqualTo(SafetySourceIssue.NOTIFICATION_BEHAVIOR_UNSPECIFIED) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getNotificationBehavior_whenSetExplicitly_returnsSpecifiedBehavior() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER) + .build() + + assertThat(safetySourceIssue.notificationBehavior) + .isEqualTo(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER) + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssue.notificationBehavior + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setNotificationBehavior_withInvalidNotificationBehavior_throwsIllegalArgumentException() { + val builder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + val exception = + assertFailsWith(IllegalArgumentException::class) { builder.setNotificationBehavior(-1) } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Unexpected NotificationBehavior for SafetySourceIssue: -1") + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssueBuilder.setNotificationBehavior(0) + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getIssueActionability_withDefaultBuilder_returnsManual() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertThat(safetySourceIssue.issueActionability) + .isEqualTo(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL) + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getIssueActionability_whenSetExplicitly_returnsValueSet() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + .build() + + assertThat(safetySourceIssue.issueActionability) + .isEqualTo(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun getIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + .build() + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssue.issueActionability + } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun setIssueActionability_withInvalidIssueActionability_throwsIllegalArgumentException() { + val builder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + val exception = + assertFailsWith(IllegalArgumentException::class) { builder.setIssueActionability(-1) } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Unexpected IssueActionability for SafetySourceIssue: -1") + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun setIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + assertFailsWith(UnsupportedOperationException::class) { + safetySourceIssueBuilder.setIssueActionability(0) + } + } + + @Test fun build_withNullId_throwsNullPointerException() { assertFailsWith(NullPointerException::class) { SafetySourceIssue.Builder( @@ -500,14 +1110,38 @@ class SafetySourceIssueTest { "Issue summary", SEVERITY_LEVEL_INFORMATION, "issue_type_id") + val exception = assertFailsWith(IllegalArgumentException::class) { builder.setIssueCategory(-1) } + assertThat(exception) .hasMessageThat() .isEqualTo("Unexpected IssueCategory for SafetySourceIssue: -1") } @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun build_withUIssueCategoryValueOnT_throwsIllegalArgumentException() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") + val builder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .addAction(action1) + + val exception = + assertFailsWith(IllegalArgumentException::class) { builder.setIssueCategory(600) } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Unexpected IssueCategory for SafetySourceIssue: 600") + } + + @Test fun build_withInvalidOnDismissPendingIntent_throwsIllegalArgumentException() { val builder = SafetySourceIssue.Builder( @@ -561,9 +1195,15 @@ class SafetySourceIssueTest { val exception = assertFailsWith(IllegalArgumentException::class) { safetySourceIssueBuilder.build() } + assertThat(exception) .hasMessageThat() - .isEqualTo("Safety source issue must contain at least 1 action") + .isEqualTo( + if (SdkLevel.isAtLeastU()) { + "Actionable safety source issue must contain at least 1 action" + } else { + "Safety source issue must contain at least 1 action" + }) } @Test @@ -587,6 +1227,57 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_withNoActionsAndManualActionabilityOnU_throwsIllegalArgumentException() { + val safetySourceIssueBuilder = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + + val exception = + assertFailsWith(IllegalArgumentException::class) { safetySourceIssueBuilder.build() } + + assertThat(exception) + .hasMessageThat() + .isEqualTo("Actionable safety source issue must contain at least 1 action") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_withNoActionsAndTipActionabilityOnU_success() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) + .build() + + assertThat(safetySourceIssue.actions).isEmpty() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_withNoActionsAndAutomaticActionabilityOnU_success() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + .build() + + assertThat(safetySourceIssue.actions).isEmpty() + } + + @Test fun describeContents_returns0() { val safetySourceIssue = SafetySourceIssue.Builder( @@ -625,7 +1316,269 @@ class SafetySourceIssueTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun parcelRoundTrip_recreatesEqual_atLeastUpsideDownCake() { + val safetySourceIssue = + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setCustomNotification( + Notification.Builder("Notification title", "Notification text") + .addAction(action2) + .build()) + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED) + .setAttributionTitle("attribution title") + .setDeduplicationId("deduplication_id") + .build() + + assertThat(safetySourceIssue).recreatesEqual(SafetySourceIssue.CREATOR) + } + + @Test fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { + newTiramisuEqualsHashCodeToStringTester().test() + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastUpsideDownCake() { + newUpsideDownCakeEqualsHashCodeToStringTester().test() + } + + /** + * Creates a new [EqualsHashCodeToStringTester] instance with all the equality groups in the + * [newTiramisuEqualsHashCodeToStringTester] plus new equality groups covering all the new + * fields added in U. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + private fun newUpsideDownCakeEqualsHashCodeToStringTester() = + newTiramisuEqualsHashCodeToStringTester() + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED) + .setCustomNotification( + Notification.Builder("Notification title", "Notification text") + .addAction(action2) + .build()) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY) + .setCustomNotification( + Notification.Builder("Other title", "Other text") + .addAction(action2) + .build()) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setAttributionTitle("attribution title") + .build(), + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setAttributionTitle("attribution title") + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_CRITICAL_WARNING, + "issue_type_id") + .setAttributionTitle("Other issue attribution title") + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setAttributionTitle("attribution title") + .setDeduplicationId("deduplication_id") + .build(), + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setAttributionTitle("attribution title") + .setDeduplicationId("deduplication_id") + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Issue subtitle") + .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) + .addAction(action1) + .addAction(action2) + .setOnDismissPendingIntent(pendingIntentService) + .setAttributionTitle("attribution title") + .setDeduplicationId("other_deduplication_id") + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Other issue subtitle") + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Other issue subtitle") + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setSubtitle("Other issue subtitle") + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL) + .addAction(action1) + .setAttributionTitle("Attribution title") + .setDeduplicationId("dedup_id") + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) + .addAction(action1) + .build(), + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + .addAction(action1) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + .build()) + .addEqualityGroup( + SafetySourceIssue.Builder( + "Issue id", + "Issue title", + "Issue summary", + SEVERITY_LEVEL_INFORMATION, + "issue_type_id") + .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED) + .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) + .build()) + + /** + * Creates a new [EqualsHashCodeToStringTester] instance which covers all the fields in the T + * API and is safe to use on any T+ API level. + */ + private fun newTiramisuEqualsHashCodeToStringTester() = EqualsHashCodeToStringTester() .addEqualityGroup( SafetySourceIssue.Builder( @@ -769,6 +1722,4 @@ class SafetySourceIssueTest { PendingIntent.getService( context, 0, Intent("Other PendingIntent service"), FLAG_IMMUTABLE)) .build()) - .test() - } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt index 73aee4dff..29d76b59b 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt @@ -32,7 +32,8 @@ class SafetyCenterConfigTest { @Test fun getSafetySourcesGroups_returnsSafetySourcesGroups() { assertThat(BASE.safetySourcesGroups) - .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN) + .containsExactly( + SafetySourcesGroupTest.STATELESS_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED) .inOrder() } @@ -41,7 +42,7 @@ class SafetyCenterConfigTest { val sourcesGroups = BASE.safetySourcesGroups assertFailsWith(UnsupportedOperationException::class) { - sourcesGroups.add(SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY) + sourcesGroups.add(SafetySourcesGroupTest.STATEFUL_INFERRED_WITH_SUMMARY) } } @@ -49,15 +50,16 @@ class SafetyCenterConfigTest { fun builder_addSafetySourcesGroup_doesNotMutatePreviouslyBuiltInstance() { val safetyCenterConfigBuilder = SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) val sourceGroups = safetyCenterConfigBuilder.build().safetySourcesGroups safetyCenterConfigBuilder.addSafetySourcesGroup( - SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY) + SafetySourcesGroupTest.STATEFUL_INFERRED_WITH_SUMMARY) assertThat(sourceGroups) - .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN) + .containsExactly( + SafetySourcesGroupTest.STATELESS_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED) .inOrder() } @@ -77,13 +79,13 @@ class SafetyCenterConfigTest { .addEqualityGroup( BASE, SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) .build()) .addEqualityGroup( SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED) .build()) .test() } @@ -91,8 +93,8 @@ class SafetyCenterConfigTest { companion object { private val BASE = SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) .build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt index 60c09fc86..66b976b31 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt @@ -17,9 +17,12 @@ package android.safetycenter.cts.config import android.content.res.Resources +import android.os.Build import android.safetycenter.config.SafetySource import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress +import com.android.modules.utils.build.SdkLevel import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows @@ -33,7 +36,7 @@ class SafetySourceTest { @Test fun getType_returnsType() { assertThat(DYNAMIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) - assertThat(DYNAMIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + assertThat(dynamicAllOptional().type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) assertThat(DYNAMIC_HIDDEN.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.type) .isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -41,40 +44,54 @@ class SafetySourceTest { assertThat(STATIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC) assertThat(STATIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC) assertThat(ISSUE_ONLY_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.type) + assertThat(issueOnlyAllOptional().type) .isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) } @Test fun getId_returnsId() { assertThat(DYNAMIC_BAREBONE.id).isEqualTo(DYNAMIC_BAREBONE_ID) - assertThat(DYNAMIC_ALL_OPTIONAL.id).isEqualTo(DYNAMIC_ALL_OPTIONAL_ID) + assertThat(dynamicAllOptional().id).isEqualTo(DYNAMIC_ALL_OPTIONAL_ID) assertThat(DYNAMIC_HIDDEN.id).isEqualTo(DYNAMIC_HIDDEN_ID) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.id).isEqualTo(DYNAMIC_HIDDEN_WITH_SEARCH_ID) assertThat(DYNAMIC_DISABLED.id).isEqualTo(DYNAMIC_DISABLED_ID) assertThat(STATIC_BAREBONE.id).isEqualTo(STATIC_BAREBONE_ID) assertThat(STATIC_ALL_OPTIONAL.id).isEqualTo(STATIC_ALL_OPTIONAL_ID) assertThat(ISSUE_ONLY_BAREBONE.id).isEqualTo(ISSUE_ONLY_BAREBONE_ID) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.id).isEqualTo(ISSUE_ONLY_ALL_OPTIONAL_ID) + assertThat(issueOnlyAllOptional().id).isEqualTo(ISSUE_ONLY_ALL_OPTIONAL_ID) } @Test fun getPackageName_returnsPackageNameOrThrows() { assertThat(DYNAMIC_BAREBONE.packageName).isEqualTo(PACKAGE_NAME) - assertThat(DYNAMIC_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME) + assertThat(dynamicAllOptional().packageName).isEqualTo(PACKAGE_NAME) assertThat(DYNAMIC_HIDDEN.packageName).isEqualTo(PACKAGE_NAME) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.packageName).isEqualTo(PACKAGE_NAME) assertThat(DYNAMIC_DISABLED.packageName).isEqualTo(PACKAGE_NAME) assertThrows(UnsupportedOperationException::class.java) { STATIC_BAREBONE.packageName } assertThrows(UnsupportedOperationException::class.java) { STATIC_ALL_OPTIONAL.packageName } assertThat(ISSUE_ONLY_BAREBONE.packageName).isEqualTo(PACKAGE_NAME) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME) + assertThat(issueOnlyAllOptional().packageName).isEqualTo(PACKAGE_NAME) + } + + @Test + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun getOptionalPackageName_returnsPackageNameOrNull() { + assertThat(DYNAMIC_BAREBONE.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(dynamicAllOptional().optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(DYNAMIC_HIDDEN.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(DYNAMIC_DISABLED.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(STATIC_BAREBONE.optionalPackageName).isNull() + assertThat(STATIC_ALL_OPTIONAL.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(ISSUE_ONLY_BAREBONE.optionalPackageName).isEqualTo(PACKAGE_NAME) + assertThat(issueOnlyAllOptional().optionalPackageName).isEqualTo(PACKAGE_NAME) } @Test fun getTitleResId_returnsTitleResIdOrThrows() { assertThat(DYNAMIC_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(DYNAMIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(dynamicAllOptional().titleResId).isEqualTo(REFERENCE_RES_ID) assertThat(DYNAMIC_DISABLED.titleResId).isEqualTo(REFERENCE_RES_ID) assertThat(DYNAMIC_HIDDEN.titleResId).isEqualTo(Resources.ID_NULL) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.titleResId).isEqualTo(REFERENCE_RES_ID) @@ -82,7 +99,7 @@ class SafetySourceTest { assertThat(STATIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.titleResId } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.titleResId + issueOnlyAllOptional().titleResId } } @@ -91,7 +108,7 @@ class SafetySourceTest { assertThrows(UnsupportedOperationException::class.java) { DYNAMIC_BAREBONE.titleForWorkResId } - assertThat(DYNAMIC_ALL_OPTIONAL.titleForWorkResId).isEqualTo(REFERENCE_RES_ID) + assertThat(dynamicAllOptional().titleForWorkResId).isEqualTo(REFERENCE_RES_ID) assertThrows(UnsupportedOperationException::class.java) { DYNAMIC_DISABLED.titleForWorkResId } @@ -105,14 +122,14 @@ class SafetySourceTest { ISSUE_ONLY_BAREBONE.titleForWorkResId } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.titleForWorkResId + issueOnlyAllOptional().titleForWorkResId } } @Test fun getSummaryResId_returnsSummaryResIdOrThrows() { assertThat(DYNAMIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID) - assertThat(DYNAMIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID) + assertThat(dynamicAllOptional().summaryResId).isEqualTo(REFERENCE_RES_ID) assertThat(DYNAMIC_DISABLED.summaryResId).isEqualTo(REFERENCE_RES_ID) assertThat(DYNAMIC_HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.summaryResId).isEqualTo(REFERENCE_RES_ID) @@ -120,14 +137,14 @@ class SafetySourceTest { assertThat(STATIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID) assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.summaryResId } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.summaryResId + issueOnlyAllOptional().summaryResId } } @Test fun getIntentAction_returnsIntentActionOrThrows() { assertThat(DYNAMIC_BAREBONE.intentAction).isEqualTo(INTENT_ACTION) - assertThat(DYNAMIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION) + assertThat(dynamicAllOptional().intentAction).isEqualTo(INTENT_ACTION) assertThat(DYNAMIC_DISABLED.intentAction).isNull() assertThat(DYNAMIC_HIDDEN.intentAction).isNull() assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.intentAction).isEqualTo(INTENT_ACTION) @@ -135,28 +152,28 @@ class SafetySourceTest { assertThat(STATIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION) assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.intentAction } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.intentAction + issueOnlyAllOptional().intentAction } } @Test fun getProfile_returnsProfile() { assertThat(DYNAMIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY) - assertThat(DYNAMIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL) + assertThat(dynamicAllOptional().profile).isEqualTo(SafetySource.PROFILE_ALL) assertThat(DYNAMIC_DISABLED.profile).isEqualTo(SafetySource.PROFILE_PRIMARY) assertThat(DYNAMIC_HIDDEN.profile).isEqualTo(SafetySource.PROFILE_ALL) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.profile).isEqualTo(SafetySource.PROFILE_ALL) assertThat(STATIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY) assertThat(STATIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL) assertThat(ISSUE_ONLY_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL) + assertThat(issueOnlyAllOptional().profile).isEqualTo(SafetySource.PROFILE_ALL) } @Test fun getInitialDisplayState_returnsInitialDisplayStateOrThrows() { assertThat(DYNAMIC_BAREBONE.initialDisplayState) .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_ENABLED) - assertThat(DYNAMIC_ALL_OPTIONAL.initialDisplayState) + assertThat(dynamicAllOptional().initialDisplayState) .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) assertThat(DYNAMIC_DISABLED.initialDisplayState) .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) @@ -174,14 +191,14 @@ class SafetySourceTest { ISSUE_ONLY_BAREBONE.initialDisplayState } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.initialDisplayState + issueOnlyAllOptional().initialDisplayState } } @Test fun getMaxSeverityLevel_returnsMaxSeverityLevelOrThrows() { assertThat(DYNAMIC_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE) - assertThat(DYNAMIC_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL) + assertThat(dynamicAllOptional().maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL) assertThat(DYNAMIC_DISABLED.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE) assertThat(DYNAMIC_HIDDEN.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE) @@ -190,13 +207,13 @@ class SafetySourceTest { STATIC_ALL_OPTIONAL.maxSeverityLevel } assertThat(ISSUE_ONLY_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL) + assertThat(issueOnlyAllOptional().maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL) } @Test fun getSearchTermsResId_returnsSearchTermsResIdOrThrows() { assertThat(DYNAMIC_BAREBONE.searchTermsResId).isEqualTo(Resources.ID_NULL) - assertThat(DYNAMIC_ALL_OPTIONAL.searchTermsResId).isEqualTo(REFERENCE_RES_ID) + assertThat(dynamicAllOptional().searchTermsResId).isEqualTo(REFERENCE_RES_ID) assertThat(DYNAMIC_DISABLED.searchTermsResId).isEqualTo(Resources.ID_NULL) assertThat(DYNAMIC_HIDDEN.searchTermsResId).isEqualTo(Resources.ID_NULL) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.searchTermsResId).isEqualTo(REFERENCE_RES_ID) @@ -206,14 +223,14 @@ class SafetySourceTest { ISSUE_ONLY_BAREBONE.searchTermsResId } assertThrows(UnsupportedOperationException::class.java) { - ISSUE_ONLY_ALL_OPTIONAL.searchTermsResId + issueOnlyAllOptional().searchTermsResId } } @Test fun isLoggingAllowed_returnsLoggingAllowedOrThrows() { assertThat(DYNAMIC_BAREBONE.isLoggingAllowed).isEqualTo(true) - assertThat(DYNAMIC_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false) + assertThat(dynamicAllOptional().isLoggingAllowed).isEqualTo(false) assertThat(DYNAMIC_DISABLED.isLoggingAllowed).isEqualTo(true) assertThat(DYNAMIC_HIDDEN.isLoggingAllowed).isEqualTo(true) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.isLoggingAllowed).isEqualTo(true) @@ -222,13 +239,13 @@ class SafetySourceTest { STATIC_ALL_OPTIONAL.isLoggingAllowed } assertThat(ISSUE_ONLY_BAREBONE.isLoggingAllowed).isEqualTo(true) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false) + assertThat(issueOnlyAllOptional().isLoggingAllowed).isEqualTo(false) } @Test fun isRefreshOnPageOpenAllowed_returnsRefreshOnPageOpenAllowedOrThrows() { assertThat(DYNAMIC_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false) - assertThat(DYNAMIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true) + assertThat(dynamicAllOptional().isRefreshOnPageOpenAllowed).isEqualTo(true) assertThat(DYNAMIC_DISABLED.isRefreshOnPageOpenAllowed).isEqualTo(false) assertThat(DYNAMIC_HIDDEN.isRefreshOnPageOpenAllowed).isEqualTo(false) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.isRefreshOnPageOpenAllowed).isEqualTo(false) @@ -239,33 +256,75 @@ class SafetySourceTest { STATIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed } assertThat(ISSUE_ONLY_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true) + assertThat(issueOnlyAllOptional().isRefreshOnPageOpenAllowed).isEqualTo(true) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @Test + fun areNotificationsAllowed_returnsNotificationsAllowed() { + assertThat(DYNAMIC_BAREBONE.areNotificationsAllowed()).isFalse() + assertThat(dynamicAllOptional().areNotificationsAllowed()).isTrue() + assertThat(DYNAMIC_DISABLED.areNotificationsAllowed()).isFalse() + assertThat(DYNAMIC_HIDDEN.areNotificationsAllowed()).isFalse() + assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.areNotificationsAllowed()).isFalse() + assertThat(STATIC_BAREBONE.areNotificationsAllowed()).isFalse() + assertThat(STATIC_ALL_OPTIONAL.areNotificationsAllowed()).isFalse() + assertThat(ISSUE_ONLY_BAREBONE.areNotificationsAllowed()).isFalse() + assertThat(issueOnlyAllOptional().areNotificationsAllowed()).isTrue() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @Test + fun getDeduplicationGroupsList_returnsDeduplicationGroups() { + assertThat(DYNAMIC_BAREBONE.deduplicationGroup).isNull() + assertThat(dynamicAllOptional().deduplicationGroup).isEqualTo(DEDUPLICATION_GROUP) + assertThat(DYNAMIC_DISABLED.deduplicationGroup).isNull() + assertThat(DYNAMIC_HIDDEN.deduplicationGroup).isNull() + assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.deduplicationGroup).isNull() + assertThat(STATIC_BAREBONE.deduplicationGroup).isNull() + assertThat(STATIC_ALL_OPTIONAL.deduplicationGroup).isNull() + assertThat(ISSUE_ONLY_BAREBONE.deduplicationGroup).isNull() + assertThat(issueOnlyAllOptional().deduplicationGroup).isEqualTo(DEDUPLICATION_GROUP) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @Test + fun getPackageCertificateHashes_returnsPackageCerts() { + assertThat(DYNAMIC_BAREBONE.packageCertificateHashes).isEmpty() + assertThat(dynamicAllOptional().packageCertificateHashes).containsExactly(HASH1) + assertThat(DYNAMIC_DISABLED.packageCertificateHashes).isEmpty() + assertThat(DYNAMIC_HIDDEN.packageCertificateHashes).isEmpty() + assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.packageCertificateHashes).isEmpty() + assertThat(STATIC_BAREBONE.packageCertificateHashes).isEmpty() + assertThat(STATIC_ALL_OPTIONAL.packageCertificateHashes).isEmpty() + assertThat(ISSUE_ONLY_BAREBONE.packageCertificateHashes).isEmpty() + assertThat(issueOnlyAllOptional().packageCertificateHashes).containsExactly(HASH1, HASH2) } @Test fun describeContents_returns0() { assertThat(DYNAMIC_BAREBONE.describeContents()).isEqualTo(0) - assertThat(DYNAMIC_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(dynamicAllOptional().describeContents()).isEqualTo(0) assertThat(DYNAMIC_DISABLED.describeContents()).isEqualTo(0) assertThat(DYNAMIC_HIDDEN.describeContents()).isEqualTo(0) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.describeContents()).isEqualTo(0) assertThat(STATIC_BAREBONE.describeContents()).isEqualTo(0) assertThat(STATIC_ALL_OPTIONAL.describeContents()).isEqualTo(0) assertThat(ISSUE_ONLY_BAREBONE.describeContents()).isEqualTo(0) - assertThat(ISSUE_ONLY_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(issueOnlyAllOptional().describeContents()).isEqualTo(0) } @Test fun parcelRoundTrip_recreatesEqual() { assertThat(DYNAMIC_BAREBONE).recreatesEqual(SafetySource.CREATOR) - assertThat(DYNAMIC_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR) + assertThat(dynamicAllOptional()).recreatesEqual(SafetySource.CREATOR) assertThat(DYNAMIC_DISABLED).recreatesEqual(SafetySource.CREATOR) assertThat(DYNAMIC_HIDDEN).recreatesEqual(SafetySource.CREATOR) assertThat(DYNAMIC_HIDDEN_WITH_SEARCH).recreatesEqual(SafetySource.CREATOR) assertThat(STATIC_BAREBONE).recreatesEqual(SafetySource.CREATOR) assertThat(STATIC_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR) assertThat(ISSUE_ONLY_BAREBONE).recreatesEqual(SafetySource.CREATOR) - assertThat(ISSUE_ONLY_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR) + assertThat(issueOnlyAllOptional()).recreatesEqual(SafetySource.CREATOR) } @Test @@ -273,7 +332,7 @@ class SafetySourceTest { EqualsHashCodeToStringTester() .addEqualityGroup(DYNAMIC_BAREBONE) .addEqualityGroup( - DYNAMIC_ALL_OPTIONAL, + dynamicAllOptional(), SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) .setId(DYNAMIC_ALL_OPTIONAL_ID) .setPackageName(PACKAGE_NAME) @@ -287,6 +346,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup(DYNAMIC_HIDDEN) .addEqualityGroup(DYNAMIC_HIDDEN_WITH_SEARCH) @@ -294,7 +360,7 @@ class SafetySourceTest { .addEqualityGroup(STATIC_BAREBONE) .addEqualityGroup(STATIC_ALL_OPTIONAL) .addEqualityGroup(ISSUE_ONLY_BAREBONE) - .addEqualityGroup(ISSUE_ONLY_ALL_OPTIONAL) + .addEqualityGroup(issueOnlyAllOptional()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) .setId("other") @@ -309,6 +375,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -324,6 +397,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -339,6 +419,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -354,6 +441,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -369,6 +463,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -384,6 +485,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -406,6 +514,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -421,6 +536,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -436,6 +558,13 @@ class SafetySourceTest { .setSearchTermsResId(-1) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -451,6 +580,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(true) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) .addEqualityGroup( SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -466,7 +602,111 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(false) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build()) + .apply { + if (SdkLevel.isAtLeastU()) { + addEqualityGroup( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + .setId(DYNAMIC_ALL_OPTIONAL_ID) + .setPackageName(PACKAGE_NAME) + .setTitleResId(REFERENCE_RES_ID) + .setTitleForWorkResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setIntentAction(INTENT_ACTION) + .setProfile(SafetySource.PROFILE_ALL) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) + .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) + .setSearchTermsResId(REFERENCE_RES_ID) + .setLoggingAllowed(false) + .setRefreshOnPageOpenAllowed(true) + .setNotificationsAllowed(false) + .setDeduplicationGroup(DEDUPLICATION_GROUP) + .addPackageCertificateHash(HASH1) + .build()) + addEqualityGroup( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + .setId(DYNAMIC_ALL_OPTIONAL_ID) + .setPackageName(PACKAGE_NAME) + .setTitleResId(REFERENCE_RES_ID) + .setTitleForWorkResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setIntentAction(INTENT_ACTION) + .setProfile(SafetySource.PROFILE_ALL) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) + .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) + .setSearchTermsResId(REFERENCE_RES_ID) + .setLoggingAllowed(false) + .setRefreshOnPageOpenAllowed(true) + .setNotificationsAllowed(true) + .setDeduplicationGroup("other_deduplication_group") + .addPackageCertificateHash(HASH1) + .build()) + // With no package cert hashes provided + addEqualityGroup( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + .setId(DYNAMIC_ALL_OPTIONAL_ID) + .setPackageName(PACKAGE_NAME) + .setTitleResId(REFERENCE_RES_ID) + .setTitleForWorkResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setIntentAction(INTENT_ACTION) + .setProfile(SafetySource.PROFILE_ALL) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) + .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) + .setSearchTermsResId(REFERENCE_RES_ID) + .setLoggingAllowed(false) + .setRefreshOnPageOpenAllowed(true) + .setNotificationsAllowed(true) + .setDeduplicationGroup(DEDUPLICATION_GROUP) + .build()) + // With longer package cert hash list + addEqualityGroup( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + .setId(DYNAMIC_ALL_OPTIONAL_ID) + .setPackageName(PACKAGE_NAME) + .setTitleResId(REFERENCE_RES_ID) + .setTitleForWorkResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setIntentAction(INTENT_ACTION) + .setProfile(SafetySource.PROFILE_ALL) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) + .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) + .setSearchTermsResId(REFERENCE_RES_ID) + .setLoggingAllowed(false) + .setRefreshOnPageOpenAllowed(true) + .setNotificationsAllowed(true) + .setDeduplicationGroup(DEDUPLICATION_GROUP) + .addPackageCertificateHash(HASH1) + .addPackageCertificateHash(HASH2) + .build()) + // With package cert hash list with different value + addEqualityGroup( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + .setId(DYNAMIC_ALL_OPTIONAL_ID) + .setPackageName(PACKAGE_NAME) + .setTitleResId(REFERENCE_RES_ID) + .setTitleForWorkResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setIntentAction(INTENT_ACTION) + .setProfile(SafetySource.PROFILE_ALL) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) + .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) + .setSearchTermsResId(REFERENCE_RES_ID) + .setLoggingAllowed(false) + .setRefreshOnPageOpenAllowed(true) + .setNotificationsAllowed(true) + .setDeduplicationGroup(DEDUPLICATION_GROUP) + .addPackageCertificateHash(HASH2) + .build()) + } + } .test() } @@ -485,6 +725,9 @@ class SafetySourceTest { private const val STATIC_ALL_OPTIONAL_ID = "static_all_optional" private const val ISSUE_ONLY_BAREBONE_ID = "issue_only_barebone" private const val ISSUE_ONLY_ALL_OPTIONAL_ID = "issue_only_all_optional" + private const val DEDUPLICATION_GROUP = "deduplication_group" + private const val HASH1 = "feed1" + private const val HASH2 = "feed2" internal val DYNAMIC_BAREBONE = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) @@ -496,7 +739,7 @@ class SafetySourceTest { .setProfile(SafetySource.PROFILE_PRIMARY) .build() - private val DYNAMIC_ALL_OPTIONAL = + private fun dynamicAllOptional(): SafetySource = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) .setId(DYNAMIC_ALL_OPTIONAL_ID) .setPackageName(PACKAGE_NAME) @@ -510,6 +753,13 @@ class SafetySourceTest { .setSearchTermsResId(REFERENCE_RES_ID) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + } + } .build() private val DYNAMIC_DISABLED = @@ -554,6 +804,7 @@ class SafetySourceTest { private val STATIC_ALL_OPTIONAL = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) .setId(STATIC_ALL_OPTIONAL_ID) + .apply { if (SdkLevel.isAtLeastU()) setPackageName(PACKAGE_NAME) } .setTitleResId(REFERENCE_RES_ID) .setTitleForWorkResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) @@ -569,7 +820,7 @@ class SafetySourceTest { .setProfile(SafetySource.PROFILE_PRIMARY) .build() - private val ISSUE_ONLY_ALL_OPTIONAL = + private fun issueOnlyAllOptional(): SafetySource = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) .setId(ISSUE_ONLY_ALL_OPTIONAL_ID) .setPackageName(PACKAGE_NAME) @@ -577,6 +828,14 @@ class SafetySourceTest { .setMaxSeverityLevel(MAX_SEVERITY_LEVEL) .setLoggingAllowed(false) .setRefreshOnPageOpenAllowed(true) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup(DEDUPLICATION_GROUP) + addPackageCertificateHash(HASH1) + addPackageCertificateHash(HASH2) + } + } .build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt index f742b5d28..28ca90b67 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt @@ -17,9 +17,13 @@ package android.safetycenter.cts.config import android.content.res.Resources +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.config.SafetySourcesGroup +import androidx.annotation.RequiresApi import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress +import com.android.modules.utils.build.SdkLevel import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith @@ -32,78 +36,156 @@ class SafetySourcesGroupTest { @Test fun getType_returnsType() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.type) - .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(COLLAPSIBLE_WITH_ICON.type) - .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(COLLAPSIBLE_WITH_BOTH.type) - .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(RIGID.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) - assertThat(HIDDEN.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + assertThat(STATEFUL_INFERRED_WITH_ICON.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + assertThat(STATEFUL_INFERRED_WITH_BOTH.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + assertThat(STATELESS_INFERRED.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + assertThat(HIDDEN_INFERRED.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + assertThat(STATEFUL_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + assertThat(STATELESS_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + assertThat(STATELESS_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + assertThat(HIDDEN_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + assertThat(HIDDEN_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + } } @Test fun getId_returnsId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.id).isEqualTo(COLLAPSIBLE_WITH_SUMMARY_ID) - assertThat(COLLAPSIBLE_WITH_ICON.id).isEqualTo(COLLAPSIBLE_WITH_ICON_ID) - assertThat(COLLAPSIBLE_WITH_BOTH.id).isEqualTo(COLLAPSIBLE_WITH_BOTH_ID) - assertThat(RIGID.id).isEqualTo(RIGID_ID) - assertThat(HIDDEN.id).isEqualTo(HIDDEN_ID) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.id).isEqualTo(STATEFUL_INFERRED_WITH_SUMMARY_ID) + assertThat(STATEFUL_INFERRED_WITH_ICON.id).isEqualTo(STATEFUL_INFERRED_WITH_ICON_ID) + assertThat(STATEFUL_INFERRED_WITH_BOTH.id).isEqualTo(STATEFUL_INFERRED_WITH_BOTH_ID) + assertThat(STATELESS_INFERRED.id).isEqualTo(STATELESS_INFERRED_ID) + assertThat(HIDDEN_INFERRED.id).isEqualTo(HIDDEN_INFERRED_ID) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.id).isEqualTo(STATEFUL_BAREBONE_ID) + assertThat(STATEFUL_ALL_OPTIONAL.id).isEqualTo(STATEFUL_ALL_OPTIONAL_ID) + assertThat(STATELESS_BAREBONE.id).isEqualTo(STATELESS_BAREBONE_ID) + assertThat(STATELESS_ALL_OPTIONAL.id).isEqualTo(STATELESS_ALL_OPTIONAL_ID) + assertThat(HIDDEN_BAREBONE.id).isEqualTo(HIDDEN_BAREBONE_ID) + assertThat(HIDDEN_ALL_OPTIONAL.id).isEqualTo(HIDDEN_ALL_OPTIONAL_ID) + } } @Test fun getTitleResId_returnsTitleResId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(RIGID.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_INFERRED_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_INFERRED_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_INFERRED.titleResId).isEqualTo(REFERENCE_RES_ID) // This is not an enforced invariant, titleResId should just be ignored for hidden groups - assertThat(HIDDEN.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_INFERRED.titleResId).isEqualTo(Resources.ID_NULL) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + } } @Test fun getSummaryResId_returnsSummaryResId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL) - assertThat(COLLAPSIBLE_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID) - assertThat(RIGID.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_INFERRED_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(STATEFUL_INFERRED_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL) // This is not an enforced invariant, summaryResId should just be ignored for hidden groups - assertThat(HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATEFUL_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(STATELESS_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + } } @Test fun getStatelessIconType_returnsStatelessIconType() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.statelessIconType) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) - assertThat(COLLAPSIBLE_WITH_ICON.statelessIconType) + assertThat(STATEFUL_INFERRED_WITH_ICON.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - assertThat(COLLAPSIBLE_WITH_BOTH.statelessIconType) + assertThat(STATEFUL_INFERRED_WITH_BOTH.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - assertThat(RIGID.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(STATELESS_INFERRED.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) // This is not an enforced invariant // statelessIconType should just be ignored for hidden groups - assertThat(HIDDEN.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(HIDDEN_INFERRED.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(STATEFUL_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + assertThat(STATELESS_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(STATELESS_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + assertThat(HIDDEN_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(HIDDEN_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } } @Test fun getSafetySources_returnsSafetySources() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.safetySources) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.safetySources) .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE) - assertThat(COLLAPSIBLE_WITH_ICON.safetySources) + assertThat(STATEFUL_INFERRED_WITH_ICON.safetySources) .containsExactly(SafetySourceTest.STATIC_BAREBONE) - assertThat(COLLAPSIBLE_WITH_BOTH.safetySources) + assertThat(STATEFUL_INFERRED_WITH_BOTH.safetySources) .containsExactly( SafetySourceTest.DYNAMIC_BAREBONE, SafetySourceTest.STATIC_BAREBONE, SafetySourceTest.ISSUE_ONLY_BAREBONE) .inOrder() - assertThat(RIGID.safetySources).containsExactly(SafetySourceTest.STATIC_BAREBONE) - assertThat(HIDDEN.safetySources).containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(STATELESS_INFERRED.safetySources) + .containsExactly(SafetySourceTest.STATIC_BAREBONE) + assertThat(HIDDEN_INFERRED.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE) + assertThat(STATEFUL_ALL_OPTIONAL.safetySources) + .containsExactly( + SafetySourceTest.DYNAMIC_BAREBONE, + SafetySourceTest.STATIC_BAREBONE, + SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(STATELESS_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.STATIC_BAREBONE) + assertThat(STATELESS_ALL_OPTIONAL.safetySources) + .containsExactly( + SafetySourceTest.DYNAMIC_BAREBONE, + SafetySourceTest.STATIC_BAREBONE, + SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(HIDDEN_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(HIDDEN_ALL_OPTIONAL.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + } } @Test fun getSafetySources_mutationsAreNotAllowed() { - val sources = COLLAPSIBLE_WITH_SUMMARY.safetySources + val sources = STATEFUL_INFERRED_WITH_SUMMARY.safetySources assertFailsWith(UnsupportedOperationException::class) { sources.add(SafetySourceTest.DYNAMIC_BAREBONE) @@ -114,7 +196,7 @@ class SafetySourcesGroupTest { fun builder_addSafetySource_doesNotMutatePreviouslyBuiltInstance() { val safetySourcesGroupBuilder = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_SUMMARY_ID) + .setId(STATEFUL_INFERRED_WITH_SUMMARY_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) @@ -126,41 +208,134 @@ class SafetySourcesGroupTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_hiddenGroupWithDynamicSource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups of type hidden can only contain sources of type issue-only") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_hiddenGroupWithStaticSource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups of type hidden can only contain sources of type issue-only") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_statefulGroupWithIssueOnlySource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + .setId(STATEFUL_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_statelessGroupWithIssueOnlySource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + .setId(STATELESS_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden") + } + + @Test fun describeContents_returns0() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.describeContents()).isEqualTo(0) - assertThat(COLLAPSIBLE_WITH_ICON.describeContents()).isEqualTo(0) - assertThat(COLLAPSIBLE_WITH_BOTH.describeContents()).isEqualTo(0) - assertThat(RIGID.describeContents()).isEqualTo(0) - assertThat(HIDDEN.describeContents()).isEqualTo(0) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY.describeContents()).isEqualTo(0) + assertThat(STATEFUL_INFERRED_WITH_ICON.describeContents()).isEqualTo(0) + assertThat(STATEFUL_INFERRED_WITH_BOTH.describeContents()).isEqualTo(0) + assertThat(STATELESS_INFERRED.describeContents()).isEqualTo(0) + assertThat(HIDDEN_INFERRED.describeContents()).isEqualTo(0) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE.describeContents()).isEqualTo(0) + assertThat(STATEFUL_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(STATELESS_BAREBONE.describeContents()).isEqualTo(0) + assertThat(STATELESS_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(HIDDEN_BAREBONE.describeContents()).isEqualTo(0) + assertThat(HIDDEN_ALL_OPTIONAL.describeContents()).isEqualTo(0) + } } @Test fun parcelRoundTrip_recreatesEqual() { - assertThat(COLLAPSIBLE_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(COLLAPSIBLE_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(COLLAPSIBLE_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(RIGID).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(HIDDEN).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATEFUL_INFERRED_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATEFUL_INFERRED_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATEFUL_INFERRED_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATELESS_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR) + if (SdkLevel.isAtLeastU()) { + assertThat(STATEFUL_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATEFUL_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATELESS_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(STATELESS_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + } } @Test fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester() - .addEqualityGroup(COLLAPSIBLE_WITH_SUMMARY) - .addEqualityGroup(COLLAPSIBLE_WITH_ICON) + .addEqualityGroup(STATEFUL_INFERRED_WITH_SUMMARY) + .addEqualityGroup(STATEFUL_INFERRED_WITH_ICON) .addEqualityGroup( - COLLAPSIBLE_WITH_BOTH, - SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) - .setTitleResId(REFERENCE_RES_ID) - .setSummaryResId(REFERENCE_RES_ID) - .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) - .addSafetySource(SafetySourceTest.STATIC_BAREBONE) - .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) - .build()) - .addEqualityGroup(RIGID) - .addEqualityGroup(HIDDEN) + *mutableListOf( + STATEFUL_INFERRED_WITH_BOTH, + SafetySourcesGroup.Builder() + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build()) + .apply { if (SdkLevel.isAtLeastU()) add(STATEFUL_ALL_OPTIONAL) } + .toTypedArray()) + .addEqualityGroup( + *mutableListOf(STATELESS_INFERRED) + .apply { if (SdkLevel.isAtLeastU()) add(STATELESS_BAREBONE) } + .toTypedArray()) + .addEqualityGroup( + *mutableListOf(HIDDEN_INFERRED) + .apply { if (SdkLevel.isAtLeastU()) add(HIDDEN_BAREBONE) } + .toTypedArray()) .addEqualityGroup( SafetySourcesGroup.Builder() .setId("other") @@ -171,7 +346,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) .setTitleResId(-1) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -179,7 +354,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(-1) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -187,7 +362,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) @@ -195,44 +370,57 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build()) + .apply { + if (SdkLevel.isAtLeastU()) { + addEqualityGroup(STATEFUL_BAREBONE) + addEqualityGroup(STATELESS_ALL_OPTIONAL) + addEqualityGroup(HIDDEN_ALL_OPTIONAL) + } + } .test() } companion object { private const val REFERENCE_RES_ID = 9999 - private const val COLLAPSIBLE_WITH_SUMMARY_ID = "collapsible_with_summary" - private const val COLLAPSIBLE_WITH_ICON_ID = "collapsible_with_icon" - private const val COLLAPSIBLE_WITH_BOTH_ID = "collapsible_with_both" - private const val RIGID_ID = "rigid" - private const val HIDDEN_ID = "hidden" + private const val STATEFUL_BAREBONE_ID = "stateful_barebone" + private const val STATEFUL_ALL_OPTIONAL_ID = "stateful_all_optional" + private const val STATELESS_BAREBONE_ID = "stateless_barebone" + private const val STATELESS_ALL_OPTIONAL_ID = "stateless_all_optional" + private const val HIDDEN_BAREBONE_ID = "hidden_barebone" + private const val HIDDEN_ALL_OPTIONAL_ID = "hidden_all_optional" + private const val STATEFUL_INFERRED_WITH_SUMMARY_ID = "stateful_inferred_with_summary" + private const val STATEFUL_INFERRED_WITH_ICON_ID = "stateful_inferred_with_icon" + private const val STATEFUL_INFERRED_WITH_BOTH_ID = STATEFUL_ALL_OPTIONAL_ID + private const val STATELESS_INFERRED_ID = STATELESS_BAREBONE_ID + private const val HIDDEN_INFERRED_ID = HIDDEN_BAREBONE_ID // TODO(b/230078826): Consider extracting shared constants to a separate file. - internal val COLLAPSIBLE_WITH_SUMMARY = + internal val STATEFUL_INFERRED_WITH_SUMMARY = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_SUMMARY_ID) + .setId(STATEFUL_INFERRED_WITH_SUMMARY_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) .build() - private val COLLAPSIBLE_WITH_ICON = + private val STATEFUL_INFERRED_WITH_ICON = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_ICON_ID) + .setId(STATEFUL_INFERRED_WITH_ICON_ID) .setTitleResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build() - private val COLLAPSIBLE_WITH_BOTH = + private val STATEFUL_INFERRED_WITH_BOTH = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(STATEFUL_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -241,17 +429,86 @@ class SafetySourcesGroupTest { .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) .build() - internal val RIGID = + private val STATEFUL_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + .setId(STATEFUL_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .build() + + private val STATEFUL_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL) + .setId(STATEFUL_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + internal val STATELESS_INFERRED = SafetySourcesGroup.Builder() - .setId(RIGID_ID) + .setId(STATELESS_INFERRED_ID) .setTitleResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build() - internal val HIDDEN = + private val STATELESS_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + .setId(STATELESS_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .build() + + private val STATELESS_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + .setId(STATELESS_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + internal val HIDDEN_INFERRED = SafetySourcesGroup.Builder() - .setId(HIDDEN_ID) + .setId(HIDDEN_INFERRED_ID) .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) .build() + + private val HIDDEN_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + private val HIDDEN_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt index 4892e6c04..da7238aa5 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt @@ -80,9 +80,16 @@ object SafetyCenterApisWithShellPermissions { * Calls [SafetyCenterManager.refreshSafetySources] adopting Shell's [MANAGE_SAFETY_CENTER] * permission. */ - fun SafetyCenterManager.refreshSafetySourcesWithPermission(refreshReason: Int) { + fun SafetyCenterManager.refreshSafetySourcesWithPermission( + refreshReason: Int, + safetySourceIds: List<String>? = null + ) { callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) { - refreshSafetySources(refreshReason) + if (safetySourceIds != null) { + refreshSafetySources(refreshReason, safetySourceIds) + } else { + refreshSafetySources(refreshReason) + } } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt index ab5db20e2..962690dca 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt @@ -18,6 +18,7 @@ package android.safetycenter.cts.testing import android.content.Context import android.content.res.Resources +import android.os.Build import android.safetycenter.SafetySourceData import android.safetycenter.config.SafetyCenterConfig import android.safetycenter.config.SafetySource @@ -26,6 +27,8 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC import android.safetycenter.config.SafetySourcesGroup import android.safetycenter.cts.testing.SettingsPackage.getSettingsPackageName +import androidx.annotation.RequiresApi +import com.android.modules.utils.build.SdkLevel /** * A class that provides [SafetyCenterConfig] objects and associated constants to facilitate setting @@ -77,6 +80,44 @@ object SafetyCenterCtsConfigs { dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setMaxSeverityLevel(0).build()) /** + * SHA256 hash of a package certificate. + * + * <p>This is a fake certificate, and can be used to test failure cases, or to test a list of + * certificates when only one match is required. + */ + const val PACKAGE_CERT_HASH_FAKE = "feed12" + + /** SHA256 hashes of the certificate(s) known to sign the CTS tests. */ + private val PACKAGE_CERT_HASHES_CTS = + listOf( + "6cecc50e34ae31bfb5678986d6d6d3736c571ded2f2459527793e1f054eb0c9b", + "a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc") + + /** An invalid SHA256 hash (not a byte string, not even number of chars). */ + const val PACKAGE_CERT_HASH_INVALID = "0124ppl" + + /** A simple [SafetyCenterConfig] for CTS tests with a fake/incorrect package cert hash. */ + val SINGLE_SOURCE_WITH_FAKE_CERT: SafetyCenterConfig + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + get() = + singleSourceConfig( + dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) + .addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) + .build()) + + /** + * A simple [SafetyCenterConfig] for CTS tests with a invalid package cert hash (not a + * hex-formatted byte string). + */ + val SINGLE_SOURCE_WITH_INVALID_CERT: SafetyCenterConfig + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + get() = + singleSourceConfig( + dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) + .addPackageCertificateHash(PACKAGE_CERT_HASH_INVALID) + .build()) + + /** * A simple [SafetyCenterConfig] for CTS tests with a source that does not support refresh on * page open. */ @@ -126,7 +167,7 @@ object SafetyCenterCtsConfigs { /** * ID of a [SafetySourcesGroup] provided by [SUMMARY_TEST_GROUP_CONFIG], containing sources: * [SOURCE_ID_1], [SOURCE_ID_2], [SOURCE_ID_3], [SOURCE_ID_4], [SOURCE_ID_5], [SOURCE_ID_6], - * [SOURCE_ID_7], [STATIC_IN_COLLAPSIBLE_ID]. + * [SOURCE_ID_7], [STATIC_IN_STATEFUL_ID]. */ const val SUMMARY_TEST_GROUP_ID = "summary_test_group_id" @@ -153,15 +194,16 @@ object SafetyCenterCtsConfigs { /** * ID of a [SafetySourcesGroup] provided by [COMPLEX_CONFIG], containing sources: - * [DYNAMIC_IN_COLLAPSIBLE_ID], [STATIC_IN_COLLAPSIBLE_ID]. + * [DYNAMIC_IN_STATEFUL_ID], [STATIC_IN_STATEFUL_ID]. */ - const val MIXED_COLLAPSIBLE_GROUP_ID = "mixed_collapsible" + const val MIXED_STATEFUL_GROUP_ID = "mixed_stateful" /** * ID of a [SafetySourcesGroup] provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], - * containing sources: [DYNAMIC_IN_RIGID_ID], [STATIC_IN_RIGID_ID], [ISSUE_ONLY_IN_RIGID_ID]. + * containing sources: [DYNAMIC_IN_STATELESS_ID], [STATIC_IN_STATELESS_ID], + * [ISSUE_ONLY_IN_STATELESS_ID]. */ - const val MIXED_RIGID_GROUP_ID = "mixed_rigid" + const val MIXED_STATELESS_GROUP_ID = "mixed_stateless" /** * ID of a source provided by [COMPLEX_CONFIG], [COMPLEX_ALL_PROFILE_CONFIG], and @@ -233,35 +275,39 @@ object SafetyCenterCtsConfigs { * ID of a source provided by [COMPLEX_CONFIG], this is a generic, dynamic, primary profile * only, visible source. */ - const val DYNAMIC_IN_COLLAPSIBLE_ID = "dynamic_in_collapsible" + const val DYNAMIC_IN_STATEFUL_ID = "dynamic_in_stateful" /** * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is a * generic, dynamic, visible source. */ - const val DYNAMIC_IN_RIGID_ID = "dynamic_in_rigid" + const val DYNAMIC_IN_STATELESS_ID = "dynamic_in_stateless" /** * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is an * issue-only source. */ - const val ISSUE_ONLY_IN_RIGID_ID = "issue_only_in_rigid" + const val ISSUE_ONLY_IN_STATELESS_ID = "issue_only_in_stateless" /** * ID of a source provided by [COMPLEX_CONFIG] and [SUMMARY_TEST_CONFIG], this is a generic, * static, primary profile only source. */ - const val STATIC_IN_COLLAPSIBLE_ID = "static_in_collapsible" + const val STATIC_IN_STATEFUL_ID = "static_in_stateful" /** * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is a * generic, static source. */ - const val STATIC_IN_RIGID_ID = "static_in_rigid" + const val STATIC_IN_STATELESS_ID = "static_in_stateless" /** Package name for the [DYNAMIC_OTHER_PACKAGE_ID] source. */ const val OTHER_PACKAGE_NAME = "other_package_name" + private const val DEDUPLICATION_GROUP_1 = "deduplication_group_1" + private const val DEDUPLICATION_GROUP_2 = "deduplication_group_2" + private const val DEDUPLICATION_GROUP_3 = "deduplication_group_3" + /** A Simple [SafetyCenterConfig] with an issue only source. */ val ISSUE_ONLY_SOURCE_CONFIG = singleSourceConfig(issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build()) @@ -271,6 +317,20 @@ object SafetyCenterCtsConfigs { singleSourceConfig( issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_PROFILE_SOURCE_ID).build()) + /** + * A Simple [SafetyCenterConfig] with an issue only source inside a [SafetySourcesGroup] with + * null title. + */ + val ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID) + .setTitleResId(Resources.ID_NULL) + .addSafetySource( + issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build()) + .build()) + .build() + /** A dynamic source with [OTHER_PACKAGE_NAME] */ val DYNAMIC_OTHER_PACKAGE_SAFETY_SOURCE = dynamicSafetySourceBuilder(DYNAMIC_OTHER_PACKAGE_ID) @@ -307,6 +367,45 @@ object SafetyCenterCtsConfigs { .build()) .build() + /** + * A simple [SafetyCenterConfig] for CTS tests with multiple sources with deduplication info. + */ + val multipleSourcesWithDeduplicationInfoConfig: SafetyCenterConfig + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + get() = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_1, DEDUPLICATION_GROUP_1)) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_2, DEDUPLICATION_GROUP_1)) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_3, DEDUPLICATION_GROUP_2)) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_4, DEDUPLICATION_GROUP_3)) + .build()) + .addSafetySourcesGroup( + safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_5, DEDUPLICATION_GROUP_1)) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_6, DEDUPLICATION_GROUP_3)) + .build()) + .addSafetySourcesGroup( + safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_3) + .addSafetySource( + issueOnlySafetySourceWithDuplicationInfo( + SOURCE_ID_7, DEDUPLICATION_GROUP_3)) + .build()) + .build() + /** Source included in [DYNAMIC_SOURCE_GROUP_1]. */ val DYNAMIC_SOURCE_1 = dynamicSafetySource(SOURCE_ID_1) @@ -452,7 +551,7 @@ object SafetyCenterCtsConfigs { SafetyCenterConfig.Builder() .addSafetySourcesGroup( safetySourcesGroupBuilder(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID) - // This is needed to have a collapsible group with an empty summary + // This is needed to have a stateful group with an empty summary .setSummaryResId(Resources.ID_NULL) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) .addSafetySource( @@ -486,7 +585,7 @@ object SafetyCenterCtsConfigs { .addSafetySource(dynamicSafetySource(SOURCE_ID_5)) .addSafetySource(dynamicSafetySource(SOURCE_ID_6)) .addSafetySource(dynamicSafetySource(SOURCE_ID_7)) - .addSafetySource(staticSafetySource(STATIC_IN_COLLAPSIBLE_ID)) + .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID)) .build()) .build() @@ -508,6 +607,16 @@ object SafetyCenterCtsConfigs { .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) .setSearchTermsResId(android.R.string.ok) .setLoggingAllowed(false) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) + PACKAGE_CERT_HASHES_CTS.forEach { + addPackageCertificateHash(it) + } + } + } .build()) .addSafetySource( dynamicSafetySourceBuilder(DYNAMIC_DISABLED_ID) @@ -532,7 +641,14 @@ object SafetyCenterCtsConfigs { .build()) .addSafetySourcesGroup( safetySourcesGroupBuilder(STATIC_GROUP_ID) - .setSummaryResId(Resources.ID_NULL) + .apply { + if (SdkLevel.isAtLeastU()) { + setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) + setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } else { + setSummaryResId(Resources.ID_NULL) + } + } .addSafetySource( staticSafetySourceBuilder(STATIC_BAREBONE_ID) .setSummaryResId(Resources.ID_NULL) @@ -540,11 +656,20 @@ object SafetyCenterCtsConfigs { .addSafetySource( staticSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID) .setSearchTermsResId(android.R.string.ok) + .apply { if (SdkLevel.isAtLeastU()) setPackageName(CTS_PACKAGE_NAME) } .build()) .build()) .addSafetySourcesGroup( SafetySourcesGroup.Builder() .setId(ISSUE_ONLY_GROUP_ID) + .apply { + if (SdkLevel.isAtLeastU()) { + setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + setTitleResId(android.R.string.ok) + setSummaryResId(android.R.string.ok) + setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } + } .addSafetySource( issueOnlySafetySourceBuilder(ISSUE_ONLY_BAREBONE_ID) .setRefreshOnPageOpenAllowed(false) @@ -553,19 +678,29 @@ object SafetyCenterCtsConfigs { issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID) .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) .setLoggingAllowed(false) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) + PACKAGE_CERT_HASHES_CTS.forEach { + addPackageCertificateHash(it) + } + } + } .build()) .build()) .addSafetySourcesGroup( - safetySourcesGroupBuilder(MIXED_COLLAPSIBLE_GROUP_ID) - .addSafetySource(dynamicSafetySource(DYNAMIC_IN_COLLAPSIBLE_ID)) - .addSafetySource(staticSafetySource(STATIC_IN_COLLAPSIBLE_ID)) + safetySourcesGroupBuilder(MIXED_STATEFUL_GROUP_ID) + .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATEFUL_ID)) + .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID)) .build()) .addSafetySourcesGroup( - safetySourcesGroupBuilder(MIXED_RIGID_GROUP_ID) + safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID) .setSummaryResId(Resources.ID_NULL) - .addSafetySource(dynamicSafetySource(DYNAMIC_IN_RIGID_ID)) - .addSafetySource(staticSafetySource(STATIC_IN_RIGID_ID)) - .addSafetySource(issueOnlySafetySource(ISSUE_ONLY_IN_RIGID_ID)) + .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATELESS_ID)) + .addSafetySource(staticSafetySource(STATIC_IN_STATELESS_ID)) + .addSafetySource(issueOnlySafetySource(ISSUE_ONLY_IN_STATELESS_ID)) .build()) .build() @@ -605,6 +740,7 @@ object SafetyCenterCtsConfigs { .addSafetySource( staticAllProfileSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID) .setSearchTermsResId(android.R.string.ok) + .apply { if (SdkLevel.isAtLeastU()) setPackageName(CTS_PACKAGE_NAME) } .build()) .build()) .addSafetySourcesGroup( @@ -618,23 +754,33 @@ object SafetyCenterCtsConfigs { issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID) .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) .setLoggingAllowed(false) + .apply { + if (SdkLevel.isAtLeastU()) { + setNotificationsAllowed(true) + setDeduplicationGroup("group") + addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) + PACKAGE_CERT_HASHES_CTS.forEach { + addPackageCertificateHash(it) + } + } + } .build()) .build()) .addSafetySourcesGroup( - safetySourcesGroupBuilder(MIXED_RIGID_GROUP_ID) + safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID) .setSummaryResId(Resources.ID_NULL) .addSafetySource( - dynamicAllProfileSafetySourceBuilder(DYNAMIC_IN_RIGID_ID).build()) + dynamicAllProfileSafetySourceBuilder(DYNAMIC_IN_STATELESS_ID).build()) .addSafetySource( - staticAllProfileSafetySourceBuilder(STATIC_IN_RIGID_ID).build()) + staticAllProfileSafetySourceBuilder(STATIC_IN_STATELESS_ID).build()) .addSafetySource( - issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_IN_RIGID_ID).build()) + issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_IN_STATELESS_ID).build()) .build()) .build() private fun dynamicSafetySource(id: String) = dynamicSafetySourceBuilder(id).build() - private fun dynamicSafetySourceBuilder(id: String) = + fun dynamicSafetySourceBuilder(id: String) = SafetySource.Builder(SAFETY_SOURCE_TYPE_DYNAMIC) .setId(id) .setPackageName(CTS_PACKAGE_NAME) @@ -664,6 +810,10 @@ object SafetyCenterCtsConfigs { .setProfile(SafetySource.PROFILE_ALL) .setTitleForWorkResId(android.R.string.paste) + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private fun issueOnlySafetySourceWithDuplicationInfo(id: String, deduplicationGroup: String) = + issueOnlySafetySourceBuilder(id).setDeduplicationGroup(deduplicationGroup).build() + private fun issueOnlySafetySource(id: String) = issueOnlySafetySourceBuilder(id).build() private fun issueOnlySafetySourceBuilder(id: String) = @@ -682,7 +832,7 @@ object SafetyCenterCtsConfigs { .setTitleResId(android.R.string.ok) .setSummaryResId(android.R.string.ok) - private fun singleSourceConfig(safetySource: SafetySource) = + fun singleSourceConfig(safetySource: SafetySource) = SafetyCenterConfig.Builder() .addSafetySourcesGroup( safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt index e0ab0bbf8..b4937f2f6 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt @@ -44,6 +44,7 @@ import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.ISSUE_TYPE import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.RECOMMENDATION_ISSUE_ACTION_ID import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.RECOMMENDATION_ISSUE_ID import android.util.ArrayMap +import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.internaldata.SafetyCenterEntryId import com.android.safetycenter.internaldata.SafetyCenterIds import com.android.safetycenter.internaldata.SafetyCenterIssueActionId @@ -74,6 +75,19 @@ class SafetyCenterCtsData(context: Context) { .build() /** + * Returns a [SafetyCenterStatus] with one alert and the given [statusResource] and + * [overallSeverityLevel]. + */ + fun safetyCenterStatusOneAlert( + statusResource: String, + overallSeverityLevel: Int + ): SafetyCenterStatus = + SafetyCenterStatus.Builder( + safetyCenterResourcesContext.getStringByName(statusResource), getAlertString(1)) + .setSeverityLevel(overallSeverityLevel) + .build() + + /** * Returns the [SafetyCenterStatus] used when the overall status is critical and no scan is in * progress for the given number of alerts. */ @@ -212,7 +226,11 @@ class SafetyCenterCtsData(context: Context) { * Returns an information [SafetyCenterIssue] for the given source and user id that is * consistent with information [SafetySourceIssue]s used in [SafetySourceCtsData]. */ - fun safetyCenterIssueInformation(sourceId: String, userId: Int = UserHandle.myUserId()) = + fun safetyCenterIssueInformation( + sourceId: String, + userId: Int = UserHandle.myUserId(), + attributionTitle: String? = "OK" + ) = SafetyCenterIssue.Builder( issueId(sourceId, INFORMATION_ISSUE_ID, userId = userId), "Information issue title", @@ -230,13 +248,18 @@ class SafetyCenterCtsData(context: Context) { "Review", safetySourceCtsData.testActivityRedirectPendingIntent) .build())) + .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) } .build() /** * Returns a recommendation [SafetyCenterIssue] for the given source and user id that is * consistent with recommendation [SafetySourceIssue]s used in [SafetySourceCtsData]. */ - fun safetyCenterIssueRecommendation(sourceId: String, userId: Int = UserHandle.myUserId()) = + fun safetyCenterIssueRecommendation( + sourceId: String, + userId: Int = UserHandle.myUserId(), + attributionTitle: String? = "OK" + ) = SafetyCenterIssue.Builder( issueId(sourceId, RECOMMENDATION_ISSUE_ID, userId = userId), "Recommendation issue title", @@ -253,6 +276,7 @@ class SafetyCenterCtsData(context: Context) { "See issue", safetySourceCtsData.testActivityRedirectPendingIntent) .build())) + .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) } .build() /** @@ -262,7 +286,8 @@ class SafetyCenterCtsData(context: Context) { fun safetyCenterIssueCritical( sourceId: String, isActionInFlight: Boolean = false, - userId: Int = UserHandle.myUserId() + userId: Int = UserHandle.myUserId(), + attributionTitle: String? = "OK" ) = SafetyCenterIssue.Builder( issueId(sourceId, CRITICAL_ISSUE_ID, userId = userId), @@ -279,6 +304,7 @@ class SafetyCenterCtsData(context: Context) { .setWillResolve(true) .setIsInFlight(isActionInFlight) .build())) + .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) } .build() /** @@ -355,5 +381,39 @@ class SafetyCenterCtsData(context: Context) { .build()) .setSafetySourceIssueActionId(sourceIssueActionId) .build()) + + /** + * On U+, returns a new [SafetyCenterData] with the dismissed issues set. Prior to U, + * returns the passed in [SafetyCenterData]. + */ + fun SafetyCenterData.withDismissedIssuesIfAtLeastU( + dismissedIssues: List<SafetyCenterIssue> + ): SafetyCenterData { + return if (SdkLevel.isAtLeastU()) + SafetyCenterData( + status, issues, entriesOrGroups, staticEntryGroups, dismissedIssues) + else this + } + + /** + * On U+, returns a new [SafetyCenterData] with [SafetyCenterIssue]s having the + * [attributionTitle]. Prior to U, returns the passed in [SafetyCenterData]. + */ + fun SafetyCenterData.withAttributionTitleInIssuesIfAtLeastU( + attributionTitle: String? + ): SafetyCenterData { + return if (SdkLevel.isAtLeastU()) { + val issuesWithAttributionTitle = + this.issues.map { + SafetyCenterIssue.Builder(it).setAttributionTitle(attributionTitle).build() + } + SafetyCenterData( + this.status, + issuesWithAttributionTitle, + this.entriesOrGroups, + this.staticEntryGroups, + this.dismissedIssues) + } else this + } } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt index c04100fb6..5f82be6fa 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt @@ -18,9 +18,11 @@ package android.safetycenter.cts.testing import android.Manifest.permission.READ_DEVICE_CONFIG import android.Manifest.permission.WRITE_DEVICE_CONFIG +import android.annotation.TargetApi import android.content.Context import android.content.pm.PackageManager import android.content.res.Resources +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.provider.DeviceConfig import android.provider.DeviceConfig.NAMESPACE_PRIVACY import android.provider.DeviceConfig.Properties @@ -28,6 +30,7 @@ import android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHA import android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT import android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN +import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK import android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED import android.safetycenter.SafetySourceData @@ -175,6 +178,19 @@ object SafetyCenterFlags { defaultValue = PackageManager.DONT_KILL_APP, IntParser()) + /** + * Flag that determines whether to show subpages in the Safety Center UI instead of the + * expand-and-collapse list. + */ + private val showSubpagesFlag = + Flag("safety_center_show_subpages", defaultValue = false, BooleanParser()) + + private val overrideRefreshOnPageOpenSourcesFlag = + Flag( + "safety_center_override_refresh_on_page_open_sources", + defaultValue = setOf(), + SetParser(StringParser())) + /** Every Safety Center flag. */ private val FLAGS: List<Flag<*>> = listOf( @@ -192,7 +208,9 @@ object SafetyCenterFlags { issueCategoryAllowlistsFlag, backgroundRefreshDeniedSourcesFlag, allowStatsdLoggingInTestsFlag, - qsTileComponentSettingFlag) + qsTileComponentSettingFlag, + showSubpagesFlag, + overrideRefreshOnPageOpenSourcesFlag) /** Returns whether the device supports Safety Center. */ fun Context.deviceSupportsSafetyCenter() = @@ -241,6 +259,12 @@ object SafetyCenterFlags { /** A property that allows getting and setting the [allowStatsdLoggingInTestsFlag]. */ var allowStatsdLoggingInTests: Boolean by allowStatsdLoggingInTestsFlag + /** A property that allows getting and setting the [showSubpagesFlag]. */ + var showSubpages: Boolean by showSubpagesFlag + + /** A property that allows getting and setting the [overrideRefreshOnPageOpenSourcesFlag]. */ + var overrideRefreshOnPageOpenSources: Set<String> by overrideRefreshOnPageOpenSourcesFlag + /** * Returns a snapshot of all the Safety Center flags. * @@ -296,14 +320,16 @@ object SafetyCenterFlags { fun Properties.isSafetyCenterEnabled() = getBoolean(isEnabledFlag.name, /* defaultValue */ false) - private fun getAllRefreshTimeoutsMap(refreshTimeout: Duration) = + @TargetApi(UPSIDE_DOWN_CAKE) + private fun getAllRefreshTimeoutsMap(refreshTimeout: Duration): Map<Int, Duration> = mapOf( REFRESH_REASON_PAGE_OPEN to refreshTimeout, REFRESH_REASON_RESCAN_BUTTON_CLICK to refreshTimeout, REFRESH_REASON_DEVICE_REBOOT to refreshTimeout, REFRESH_REASON_DEVICE_LOCALE_CHANGE to refreshTimeout, REFRESH_REASON_SAFETY_CENTER_ENABLED to refreshTimeout, - REFRESH_REASON_OTHER to refreshTimeout) + REFRESH_REASON_OTHER to refreshTimeout, + REFRESH_REASON_PERIODIC to refreshTimeout) private interface Parser<T> { fun parseFromString(stringValue: String): T diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt index ce022b5a6..398c25e00 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt @@ -20,6 +20,8 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.Intent.FLAG_RECEIVER_FOREGROUND +import android.content.pm.PackageManager.ResolveInfoFlags +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceData import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING @@ -38,6 +40,8 @@ import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.ACTI import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ID import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ACTION_ID import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ID +import androidx.annotation.RequiresApi +import java.lang.IllegalStateException import kotlin.math.max /** @@ -77,7 +81,12 @@ class SafetySourceCtsData(private val context: Context) { .build() /** A [SafetySourceIssue] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. */ - val informationIssue = + val informationIssue = defaultInformationIssueBuilder().build() + + /** + * A [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. + */ + private fun defaultInformationIssueBuilder() = SafetySourceIssue.Builder( INFORMATION_ISSUE_ID, "Information issue title", @@ -88,7 +97,6 @@ class SafetySourceCtsData(private val context: Context) { Action.Builder( INFORMATION_ISSUE_ACTION_ID, "Review", testActivityRedirectPendingIntent) .build()) - .build() /** * A [SafetySourceIssue] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. With @@ -186,6 +194,24 @@ class SafetySourceCtsData(private val context: Context) { .build() /** + * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting a [SafetySourceIssue] + * having a [SafetySourceIssue.mAttributionTitle] and [SafetySourceStatus]. + */ + val informationWithIssueWithAttributionTitle: SafetySourceData + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourceData.Builder() + .setStatus( + SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) + .setPendingIntent(testActivityRedirectPendingIntent) + .build()) + .addIssue( + defaultInformationIssueBuilder() + .setAttributionTitle("Attribution Title") + .build()) + .build() + + /** * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and * [SafetySourceStatus], to be used for a managed profile entry. */ @@ -240,6 +266,14 @@ class SafetySourceCtsData(private val context: Context) { val recommendationGeneralIssue = defaultRecommendationIssueBuilder().build() /** + * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], general category, redirecting + * [Action] and with deduplication id. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + fun recommendationIssueWithDeduplicationId(deduplicationId: String) = + defaultRecommendationIssueBuilder().setDeduplicationId(deduplicationId).build() + + /** * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], account category and a * redirecting [Action]. */ @@ -396,6 +430,14 @@ class SafetySourceCtsData(private val context: Context) { val criticalResolvingGeneralIssue = defaultCriticalResolvingIssueBuilder().build() /** + * General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and with deduplication + * info and a resolving [Action]. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + fun criticalIssueWithDeduplicationId(deduplicationId: String) = + defaultCriticalResolvingIssueBuilder().setDeduplicationId(deduplicationId).build() + + /** * Account related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving * [Action]. */ @@ -569,8 +611,23 @@ class SafetySourceCtsData(private val context: Context) { } /** Returns a [PendingIntent] that redirects to [intent]. */ - fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent = - PendingIntent.getActivity( - context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE) + fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent { + val explicitIntent = Intent(intent).setPackage(context.packageName) + val redirectIntent = + if (intentResolves(context, intent)) { + intent + } else if (intentResolves(context, explicitIntent)) { + explicitIntent + } else { + throw IllegalStateException("Intent doesn't resolve") + } + return PendingIntent.getActivity( + context, 0 /* requestCode */, redirectIntent, PendingIntent.FLAG_IMMUTABLE) + } + + private fun intentResolves(context: Context, intent: Intent): Boolean = + context.packageManager + .queryIntentActivities(intent, ResolveInfoFlags.of(0)) + .isNotEmpty() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt index 12b68fd4c..c377a2b3c 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt @@ -139,17 +139,20 @@ class SafetySourceReceiver : BroadcastReceiver() { fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( refreshReason: Int, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, + safetySourceIds: List<String>? = null ) = callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { - refreshSafetySourcesWithoutReceiverPermissionAndWait(refreshReason, timeout) + refreshSafetySourcesWithoutReceiverPermissionAndWait( + refreshReason, timeout, safetySourceIds) } fun SafetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait( refreshReason: Int, - timeout: Duration + timeout: Duration, + safetySourceIds: List<String>? = null ): String { - refreshSafetySourcesWithPermission(refreshReason) + refreshSafetySourcesWithPermission(refreshReason, safetySourceIds) if (timeout < TIMEOUT_LONG) { getApplicationContext().waitForBroadcastIdle() } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt index ceb700e72..cfe013466 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt @@ -17,6 +17,9 @@ package android.safetycenter.cts.ui import android.content.Context +import android.os.Build.VERSION.CODENAME +import android.os.Build.VERSION_CODES.TIRAMISU +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.Bundle import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID @@ -33,6 +36,8 @@ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_3 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_1 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_2 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_3 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG_WITH_SOURCE_WITH_INVALID_INTENT import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCE_GROUPS_CONFIG @@ -69,11 +74,13 @@ import android.support.test.uiautomator.By import android.support.test.uiautomator.UiDevice import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress import com.android.compatibility.common.util.DisableAnimationRule import com.android.compatibility.common.util.FreezeRotationRule import com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice import java.time.Duration import org.junit.After +import org.junit.Assume.assumeFalse import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Rule @@ -742,6 +749,52 @@ class SafetyCenterActivityTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun issueCard_withAttributionTitleSetBySource_displaysAttributionTitle() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + + val data = safetySourceCtsData.informationWithIssueWithAttributionTitle + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data) + + context.launchSafetyCenterActivity { waitAllTextDisplayed("Attribution Title") } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun issueCard_attributionNotSetBySource_displaysGroupTitleAsAttribution() { + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + + val data = safetySourceCtsData.recommendationWithGeneralIssue + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data) + + context.launchSafetyCenterActivity { waitAllTextDisplayed("OK") } + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun issueCard_attributionNotSetBySourceAndGroupTitleNull_doesNotDisplayAttributionTitle() { + safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG) + + val data = SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue) + safetyCenterCtsHelper.setData(ISSUE_ONLY_ALL_OPTIONAL_ID, data) + + context.launchSafetyCenterActivity { waitAllTextNotDisplayed("Attribution Title", "OK") } + } + + @Test + @SdkSuppress(maxSdkVersion = TIRAMISU) + fun issueCard_attributionNotSetBySourceOnTiramisu_doesNotDisplayAttributionTitle() { + // TODO(b/258228790): Remove after U is no longer in pre-release + assumeFalse(CODENAME == "UpsideDownCake") + safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG) + + val data = safetySourceCtsData.recommendationWithGeneralIssue + safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data) + + context.launchSafetyCenterActivity { waitAllTextNotDisplayed("Attribution title", "OK") } + } + + @Test fun launchActivity_fromQuickSettings_issuesExpanded() { safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG) safetyCenterCtsHelper.setData( diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt index 057bc2a51..7b42e59f1 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt @@ -34,8 +34,6 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compatibility.common.util.DisableAnimationRule import com.android.compatibility.common.util.FreezeRotationRule -import com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice -import java.time.Duration import org.junit.After import org.junit.Assume.assumeTrue import org.junit.Before @@ -125,8 +123,6 @@ class SafetyCenterQsActivityTest { waitDisplayed(By.desc("Switch. Mic access. Available")) { it.click() } // Verify updated state of privacy controls - getUiDevice() - .waitForWindowUpdate(/* from any window*/ null, DATA_UPDATE_TIMEOUT.toMillis()) waitDisplayed(By.desc("Switch. Camera access. Blocked")) waitDisplayed(By.desc("Switch. Mic access. Blocked")) } @@ -152,8 +148,4 @@ class SafetyCenterQsActivityTest { sensorPrivacyManager.setSensorPrivacy(sensor, disableSensor) } } - - companion object { - private val DATA_UPDATE_TIMEOUT = Duration.ofSeconds(25) - } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt new file mode 100644 index 000000000..628917243 --- /dev/null +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.safetycenter.cts.ui + +import android.content.Context +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.os.Bundle +import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID +import android.safetycenter.config.SafetySourcesGroup +import android.safetycenter.cts.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_GROUP_ID_1 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCE_GROUPS_CONFIG +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_1 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_4 +import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_5 +import android.safetycenter.cts.testing.SafetyCenterCtsHelper +import android.safetycenter.cts.testing.SafetyCenterFlags +import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter +import android.safetycenter.cts.testing.SafetySourceCtsData +import android.safetycenter.cts.testing.UiTestHelper.waitAllTextDisplayed +import android.safetycenter.cts.testing.UiTestHelper.waitDisplayed +import android.safetycenter.cts.testing.UiTestHelper.waitNotDisplayed +import android.support.test.uiautomator.By +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress +import com.android.compatibility.common.util.DisableAnimationRule +import com.android.compatibility.common.util.FreezeRotationRule +import com.android.compatibility.common.util.UiAutomatorUtils +import org.junit.After +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** CTS tests for generic subpages in Safety Center. */ +@RunWith(AndroidJUnit4::class) +@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") +class SafetyCenterSubpagesTest { + + @get:Rule val disableAnimationRule = DisableAnimationRule() + + @get:Rule val freezeRotationRule = FreezeRotationRule() + + private val context: Context = getApplicationContext() + private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context) + private val safetySourceCtsData = SafetySourceCtsData(context) + + // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to + // manually skip the setup and teardown methods. + private val shouldRunTests = context.deviceSupportsSafetyCenter() + + @Before + fun assumeDeviceSupportsSafetyCenterToRunTests() { + assumeTrue(shouldRunTests) + } + + @Before + fun enableSafetyCenterBeforeTest() { + if (!shouldRunTests) { + return + } + safetyCenterCtsHelper.setup() + SafetyCenterFlags.showSubpages = true + } + + @After + fun clearDataAfterTest() { + if (!shouldRunTests) { + return + } + safetyCenterCtsHelper.reset() + } + + @Test + fun launchSafetyCenter_withSubpagesIntentExtra_showsSubpageTitle() { + safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + val extras = Bundle() + extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1) + + context.launchSafetyCenterActivity(extras) { + // CollapsingToolbar title can't be found by text, so using description instead. + waitDisplayed( + By.desc( + context.getString( + MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()!!.titleResId))) + } + } + + @Test + fun launchSafetyCenter_withSubpagesIntentExtraButFlagDisabled_showsHomepageTitle() { + SafetyCenterFlags.showSubpages = false + safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + val extras = Bundle() + extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1) + + context.launchSafetyCenterActivity(extras) { + // CollapsingToolbar title can't be found by text, so using description instead. + waitDisplayed(By.desc("Security & privacy")) + } + } + + @Test + fun launchSafetyCenter_withNonExistingGroupID_displaysNothing() { + safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + val extras = Bundle() + extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, "non_existing_group_id") + + context.launchSafetyCenterActivity(extras) { + waitNotDisplayed( + By.desc( + context.getString( + MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()!!.titleResId))) + } + } + + @Test + fun launchSafetyCenter_withMultipleGroups_showsHomepageEntries() { + val sourceCtsData = safetySourceCtsData.information + with(safetyCenterCtsHelper) { + setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + setData(SOURCE_ID_1, sourceCtsData) + setData(SOURCE_ID_2, sourceCtsData) + setData(SOURCE_ID_3, sourceCtsData) + setData(SOURCE_ID_4, sourceCtsData) + setData(SOURCE_ID_5, sourceCtsData) + } + val firstGroup: SafetySourcesGroup = + MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first() + val lastGroup: SafetySourcesGroup = MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.last() + + context.launchSafetyCenterActivity { + waitDisplayed(By.text(context.getString(firstGroup.titleResId))) + waitDisplayed(By.text(context.getString(firstGroup.summaryResId))) + waitDisplayed(By.text(context.getString(lastGroup.titleResId))) + waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() } + + // Verifying that the subpage is opened with collapsing toolbar title + waitDisplayed(By.desc(context.getString(lastGroup.titleResId))) + waitNotDisplayed(By.text(context.getString(lastGroup.summaryResId))) + } + } + + @Test + fun launchSafetyCenter_withMultipleGroupsButFlagDisabled_showsExpandAndCollapseEntries() { + SafetyCenterFlags.showSubpages = false + val sourceCtsData = safetySourceCtsData.information + with(safetyCenterCtsHelper) { + setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + setData(SOURCE_ID_1, sourceCtsData) + setData(SOURCE_ID_2, sourceCtsData) + setData(SOURCE_ID_3, sourceCtsData) + setData(SOURCE_ID_4, sourceCtsData) + setData(SOURCE_ID_5, sourceCtsData) + } + val firstGroup: SafetySourcesGroup = + MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first() + val lastGroup: SafetySourcesGroup = MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.last() + + context.launchSafetyCenterActivity { + waitDisplayed(By.text(context.getString(firstGroup.titleResId))) + waitDisplayed(By.text(context.getString(firstGroup.summaryResId))) + waitDisplayed(By.text(context.getString(lastGroup.titleResId))) + waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() } + + // Verifying that the group is expanded and sources are displayed + waitAllTextDisplayed(sourceCtsData.status!!.title, sourceCtsData.status!!.summary) + waitNotDisplayed(By.text(context.getString(lastGroup.summaryResId))) + } + } + + @Test + fun launchSafetyCenter_redirectBackFromSubpage_showsHomepageEntries() { + with(safetyCenterCtsHelper) { + setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG) + setData(SOURCE_ID_1, safetySourceCtsData.information) + setData(SOURCE_ID_2, safetySourceCtsData.information) + } + val firstGroup: SafetySourcesGroup = + MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first() + + context.launchSafetyCenterActivity { + // Verifying that both entry title and summary are displayed on homepage + waitDisplayed(By.text(context.getString(firstGroup.titleResId))) + waitDisplayed(By.text(context.getString(firstGroup.summaryResId))) { it.click() } + + // Verifying that only collapsing toolbar title is displayed for subpage + waitDisplayed(By.desc(context.getString(firstGroup.titleResId))) + waitNotDisplayed(By.text(context.getString(firstGroup.summaryResId))) + + // Verifying that clicking on the back button opens homepage again + UiAutomatorUtils.getUiDevice().pressBack() + waitDisplayed(By.text(context.getString(firstGroup.titleResId))) + waitDisplayed(By.text(context.getString(firstGroup.summaryResId))) + } + } +} |