diff options
632 files changed, 22575 insertions, 5437 deletions
diff --git a/Android.bp b/Android.bp index 9b1857741..f2aab3568 100644 --- a/Android.bp +++ b/Android.bp @@ -110,6 +110,7 @@ bootclasspath_fragment { package_prefixes: [ "android.app.role", "android.app.ecm", + "android.permission.internal", "android.permission.jarjar", "android.safetycenter", "android.safetylabel", diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp index a8d2f846f..397870c20 100644 --- a/PermissionController/Android.bp +++ b/PermissionController/Android.bp @@ -72,6 +72,11 @@ java_library { ], } +filegroup { + name: "PermissionController-jarjar-rules", + srcs: ["jarjar-rules.txt"], +} + android_library { name: "PermissionController-lib", sdk_version: "system_current", @@ -84,6 +89,7 @@ android_library { ], //javacflags: ["-Werror"], + kotlin_lang_version: "1.9", kotlincflags: [ "-Werror", "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi", @@ -112,7 +118,6 @@ android_library { "androidx.legacy_legacy-preference-v14", "androidx.leanback_leanback", "androidx.leanback_leanback-preference", - "androidx.lifecycle_lifecycle-extensions", "androidx.lifecycle_lifecycle-common-java8", "kotlin-stdlib", "kotlinx-coroutines-android", @@ -154,6 +159,7 @@ android_library { "androidx.compose.runtime_runtime-livedata", "androidx.compose.ui_ui", "androidx.wear.compose_compose-material", + "androidx.wear.compose_compose-material3", "android.content.pm.flags-aconfig-java-export", "android.os.flags-aconfig-java-export", ], @@ -169,7 +175,7 @@ android_library { ], // TODO(b/313706381): Remove jarjar once flagging lib is fixed - jarjar_rules: "jarjar-rules.txt", + jarjar_rules: ":PermissionController-jarjar-rules", } android_app { diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml index c0d6c2907..9eeef93a2 100644 --- a/PermissionController/AndroidManifest.xml +++ b/PermissionController/AndroidManifest.xml @@ -65,10 +65,10 @@ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" /> <uses-permission android:name="android.permission.GET_APP_METADATA" /> - <uses-permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO" /> <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" /> <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" /> <uses-permission android:name="android.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION" /> + <uses-permission android:name="android.permission.WATCH_APPOPS" /> <application android:name="com.android.permissioncontroller.PermissionControllerApplication" android:label="@string/app_name" diff --git a/PermissionController/OWNERS b/PermissionController/OWNERS index 3ed65e519..aceac6afa 100644 --- a/PermissionController/OWNERS +++ b/PermissionController/OWNERS @@ -4,8 +4,10 @@ include platform/frameworks/base:/core/java/android/permission/OWNERS stenning@google.com # For television related changes -aabdagic@google.com -robhor@google.com +per-file TELEVISION_OWNERS = file:/PermissionController/TELEVISION_OWNERS +per-file src/com/android/permissioncontroller/permission/ui/television/** = file:/PermissionController/TELEVISION_OWNERS +per-file res/*-television/* = file:/PermissionController/TELEVISION_OWNERS +per-file tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/** = file:/PermissionController/TELEVISION_OWNERS # For incident report related changes joeo@google.com @@ -15,7 +17,9 @@ per-file res/** = file:platform/packages/modules/Permission:/SafetyCenter/OWNERS # For Wear related changes per-file WEAR_OWNERS = file:/PermissionController/WEAR_OWNERS +per-file wear-permission-components/** = file:/PermissionController/WEAR_OWNERS per-file src/com/android/permissioncontroller/permission/ui/wear/** = file:/PermissionController/WEAR_OWNERS per-file src/com/android/permissioncontroller/role/ui/wear/** = file:/PermissionController/WEAR_OWNERS +per-file src/com/android/permissioncontroller/incident/wear/** = file:/PermissionController/WEAR_OWNERS per-file res/*-watch/* = file:/PermissionController/WEAR_OWNERS per-file tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/** = file:/PermissionController/WEAR_OWNERS diff --git a/PermissionController/TELEVISION_OWNERS b/PermissionController/TELEVISION_OWNERS new file mode 100644 index 000000000..af786d751 --- /dev/null +++ b/PermissionController/TELEVISION_OWNERS @@ -0,0 +1,2 @@ +bronger@google.com +timurc@google.com
\ No newline at end of file diff --git a/PermissionController/WEAR_OWNERS b/PermissionController/WEAR_OWNERS index da9486f1c..c8fabba56 100644 --- a/PermissionController/WEAR_OWNERS +++ b/PermissionController/WEAR_OWNERS @@ -1,3 +1,4 @@ adsule@google.com sadrul@google.com youngjoonyang@google.com +vigneshrsastra@google.com diff --git a/PermissionController/jarjar-rules.txt b/PermissionController/jarjar-rules.txt index 4df97f4c0..74303a439 100644 --- a/PermissionController/jarjar-rules.txt +++ b/PermissionController/jarjar-rules.txt @@ -1,2 +1,33 @@ -rule android.permission.flags.*Flags* com.android.permissioncontroller.jarjar.@0 +# You may bypass this Gerrit IfThisThenThat Lint if your change doesn't affect +# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit +# message. +# LINT.IfChange +rule android.app.admin.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.app.admin.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.app.admin.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule android.app.admin.flags.Flags com.android.permissioncontroller.jarjar.@0 +rule android.app.appfunctions.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.app.appfunctions.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.app.appfunctions.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule android.app.appfunctions.flags.Flags com.android.permissioncontroller.jarjar.@0 +rule android.companion.virtualdevice.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.companion.virtualdevice.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.companion.virtualdevice.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule android.companion.virtualdevice.flags.Flags com.android.permissioncontroller.jarjar.@0 +rule android.content.pm.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.content.pm.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.content.pm.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule android.content.pm.Flags com.android.permissioncontroller.jarjar.@0 +rule android.permission.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.permission.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.permission.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0 rule android.permission.flags.Flags com.android.permissioncontroller.jarjar.@0 +rule android.os.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.os.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule android.os.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule android.os.Flags com.android.permissioncontroller.jarjar.@0 +rule com.android.permission.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule com.android.permission.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0 +rule com.android.permission.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0 +rule com.android.permission.flags.Flags com.android.permissioncontroller.jarjar.@0 +# LINT.ThenChange(PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java:applyJarjarTransform) diff --git a/PermissionController/proguard.flags b/PermissionController/proguard.flags index 292e3e4f4..52011e70c 100644 --- a/PermissionController/proguard.flags +++ b/PermissionController/proguard.flags @@ -32,6 +32,11 @@ *** has*(); } +# Keep Flags classes because they may be referenced by reflection in roles.xml +-keep class **.Flags { + *; +} + # Strip verbose logs. -assumenosideeffects class android.util.Log { static *** v(...); diff --git a/PermissionController/res/drawable-v31/auto_grant_permissions_buttons.xml b/PermissionController/res/drawable-v31/auto_grant_permissions_buttons.xml new file mode 100644 index 000000000..2b82388fb --- /dev/null +++ b/PermissionController/res/drawable-v31/auto_grant_permissions_buttons.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners + android:bottomLeftRadius="@dimen/car_location_permission_prompt_radius" + android:bottomRightRadius="@dimen/car_location_permission_prompt_radius" + android:topLeftRadius="@dimen/car_location_permission_prompt_radius" + android:topRightRadius="@dimen/car_location_permission_prompt_radius" /> + <solid android:color="@android:color/system_accent2_200" /> +</shape> diff --git a/PermissionController/res/drawable-v31/auto_grant_permissions_dialog.xml b/PermissionController/res/drawable-v31/auto_grant_permissions_dialog.xml new file mode 100644 index 000000000..bd96d07f0 --- /dev/null +++ b/PermissionController/res/drawable-v31/auto_grant_permissions_dialog.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners + android:bottomLeftRadius="@dimen/car_location_permission_prompt_radius" + android:bottomRightRadius="@dimen/car_location_permission_prompt_radius" + android:topLeftRadius="@dimen/car_location_permission_prompt_radius" + android:topRightRadius="@dimen/car_location_permission_prompt_radius" /> + <solid android:color="@android:color/system_neutral1_800" /> +</shape> diff --git a/PermissionController/res/drawable/ic_nfc.xml b/PermissionController/res/drawable/ic_nfc.xml new file mode 100644 index 000000000..5c7cdadb4 --- /dev/null +++ b/PermissionController/res/drawable/ic_nfc.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:tint="?android:attr/colorControlNormal"> + + <path + android:fillColor="@android:color/white" + android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20L4,20L4,4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72L13,8h3v8L8,16L8,8h2L10,6L6,6v12h12L18,6z" /> +</vector> diff --git a/PermissionController/res/drawable/ic_shield_exclamation_outline_24dp.xml b/PermissionController/res/drawable/ic_shield_exclamation_outline_24dp.xml new file mode 100644 index 000000000..6bbf7a174 --- /dev/null +++ b/PermissionController/res/drawable/ic_shield_exclamation_outline_24dp.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + 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_revoke.xml b/PermissionController/res/layout-television/preference_permissions_revoke.xml index 439768fdf..91740d261 100644 --- a/PermissionController/res/layout-television/preference_permissions_revoke.xml +++ b/PermissionController/res/layout-television/preference_permissions_revoke.xml @@ -41,8 +41,8 @@ android:visibility="gone"> <ImageView android:id="@android:id/icon" - android:layout_width="@dimen/permission_icon_size" - android:layout_height="@dimen/permission_icon_size"/> + android:layout_width="@dimen/tv_permission_icon_size" + android:layout_height="@dimen/tv_permission_icon_size"/> </LinearLayout> <TextView diff --git a/PermissionController/res/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml index e6d749142..107c778a1 100644 --- a/PermissionController/res/layout-v33/preference_issue_card.xml +++ b/PermissionController/res/layout-v33/preference_issue_card.xml @@ -13,81 +13,84 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<androidx.constraintlayout.widget.ConstraintLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/issue_card" - android:clickable="false" - android:screenReaderFocusable="true" - style="@style/SafetyCenterCard.Issue"> + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <androidx.constraintlayout.widget.ConstraintLayout + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/issue_card" + android:clickable="false" + android:screenReaderFocusable="true" + style="@style/SafetyCenterCard.Issue"> - <ImageButton - android:id="@+id/issue_card_dismiss_btn" - android:src="@drawable/ic_safety_issue_dismiss" - android:contentDescription="@string/safety_center_issue_card_dismiss_button" - style="@style/SafetyCenterIssueDismiss" /> + <ImageButton + android:id="@+id/issue_card_dismiss_btn" + android:src="@drawable/ic_safety_issue_dismiss" + android:contentDescription="@string/safety_center_issue_card_dismiss_button" + style="@style/SafetyCenterIssueDismiss" /> - <TextView - android:id="@+id/issue_card_attribution_title" - android:text="@string/summary_placeholder" - android:screenReaderFocusable="false" - style="@style/SafetyCenterIssueAttributionTitle" /> + <TextView + android:id="@+id/issue_card_attribution_title" + android:text="@string/summary_placeholder" + android:screenReaderFocusable="false" + style="@style/SafetyCenterIssueAttributionTitle" /> - <TextView - android:id="@+id/issue_card_title" - android:text="@string/summary_placeholder" - android:screenReaderFocusable="false" - style="@style/SafetyCenterIssueTitle" /> + <TextView + android:id="@+id/issue_card_title" + android:text="@string/summary_placeholder" + android:screenReaderFocusable="false" + style="@style/SafetyCenterIssueTitle" /> - <TextView - android:id="@+id/issue_card_subtitle" - android:text="@string/summary_placeholder" - android:screenReaderFocusable="false" - style="@style/SafetyCenterIssueSubtitle" /> + <TextView + android:id="@+id/issue_card_subtitle" + android:text="@string/summary_placeholder" + android:screenReaderFocusable="false" + style="@style/SafetyCenterIssueSubtitle" /> - <TextView - android:id="@+id/issue_card_summary" - android:text="@string/summary_placeholder" - android:screenReaderFocusable="false" - style="@style/SafetyCenterIssueSummary" /> + <TextView + android:id="@+id/issue_card_summary" + android:text="@string/summary_placeholder" + android:screenReaderFocusable="false" + style="@style/SafetyCenterIssueSummary" /> - <include - android:id="@+id/issue_card_action_button_list" - layout="?attr/scActionButtonListLayout"/> + <include + android:id="@+id/issue_card_action_button_list" + layout="?attr/scActionButtonListLayout"/> - <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView - android:id="@+id/issue_card_protected_by_android" - android:importantForAccessibility="no" - style="@style/SafetyCenterIssueSafetyProtectionSection" /> + <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView + android:id="@+id/issue_card_protected_by_android" + android:importantForAccessibility="no" + style="@style/SafetyCenterIssueSafetyProtectionSection" /> - <ImageView - android:id="@+id/resolved_issue_image" - android:src="@drawable/safety_center_issue_resolved_avd" - android:importantForAccessibility="no" - style="@style/SafetyCenterIssueCardResolvedImage" /> + <ImageView + android:id="@+id/resolved_issue_image" + android:src="@drawable/safety_center_issue_resolved_avd" + android:importantForAccessibility="no" + style="@style/SafetyCenterIssueCardResolvedImage" /> - <TextView - android:id="@+id/resolved_issue_text" - android:text="@string/safety_center_resolved_issue_fallback" - style="@style/SafetyCenterIssueCardResolvedTitle" /> + <TextView + android:id="@+id/resolved_issue_text" + android:text="@string/safety_center_resolved_issue_fallback" + style="@style/SafetyCenterIssueCardResolvedTitle" /> - <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn, - issue_card_subtitle or issue_card_protected_by_android since the version of - ConstraintLayout we're using doesn't allow us to override the group's visibility on - individual group members. See b/242705351 for context. --> - <androidx.constraintlayout.widget.Group - android:id="@+id/default_issue_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="visible" - app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" /> + <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn, + issue_card_subtitle or issue_card_protected_by_android since the version of + ConstraintLayout we're using doesn't allow us to override the group's visibility on + individual group members. See b/242705351 for context. --> + <androidx.constraintlayout.widget.Group + android:id="@+id/default_issue_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="visible" + app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" /> - <androidx.constraintlayout.widget.Group - android:id="@+id/resolved_issue_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="gone" - app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" /> + <androidx.constraintlayout.widget.Group + android:id="@+id/resolved_issue_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" /> -</androidx.constraintlayout.widget.ConstraintLayout> + </androidx.constraintlayout.widget.ConstraintLayout> +</FrameLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout-v33/preference_more_issues_card.xml b/PermissionController/res/layout-v33/preference_more_issues_card.xml index c93125762..d0ac6b1f5 100644 --- a/PermissionController/res/layout-v33/preference_more_issues_card.xml +++ b/PermissionController/res/layout-v33/preference_more_issues_card.xml @@ -13,8 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/issue_card" - style="@style/SafetyCenterMoreIssuesCollapsed"/> + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView + android:id="@+id/more_issues_card" + style="@style/SafetyCenterMoreIssuesCollapsed"/> +</FrameLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout-v33/preference_safety_status.xml b/PermissionController/res/layout-v33/preference_safety_status.xml index 42bf1c22d..17adc035f 100644 --- a/PermissionController/res/layout-v33/preference_safety_status.xml +++ b/PermissionController/res/layout-v33/preference_safety_status.xml @@ -14,7 +14,12 @@ ~ limitations under the License. --> -<com.android.permissioncontroller.safetycenter.ui.view.StatusCardView +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:clickable="false" - style="@style/SafetyCenterCard.Status" />
\ No newline at end of file + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.android.permissioncontroller.safetycenter.ui.view.StatusCardView + android:id="@+id/status_card" + android:clickable="false" + style="@style/SafetyCenterCard.Status" /> +</FrameLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml b/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml new file mode 100644 index 000000000..75381e762 --- /dev/null +++ b/PermissionController/res/layout-v35/app_permission_footer_link_preference.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- + ~ A copy of PermissionPreference, with custom styles for some elements + --> +<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/AppPermissionFooterLinkPreferenceRootLayoutStyle"> + + <RelativeLayout + style="@style/AppPermissionFooterLinkPreferenceTextLayoutStyle"> + + <TextView + android:id="@android:id/summary" + style="@style/AppPermissionFooterLinkPreferenceSummaryStyle"/> + + </RelativeLayout> + +</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout> diff --git a/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml b/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml new file mode 100644 index 000000000..230e51fc3 --- /dev/null +++ b/PermissionController/res/layout-v35/permission_preference_selector_with_widget.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<!-- A direct copy of the following layout, but with overlayable styles: + ~ SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml + --> +<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/PermissionSelectorWithWidgetPreferenceRootLayoutStyle"> + + <LinearLayout + android:id="@android:id/widget_frame" + style="@style/PermissionSelectorWithWidgetPreferenceWidgetFrameStyle" /> + + <LinearLayout + android:id="@+id/icon_frame" + style="@style/PermissionSelectorWithWidgetPreferenceIconFrameStyle"> + + <androidx.preference.internal.PreferenceImageView + android:id="@android:id/icon" + style="@style/PermissionSelectorWithWidgetPreferenceIconStyle" /> + </LinearLayout> + + <LinearLayout style="@style/PermissionSelectorWithWidgetPreferenceTextContainerStyle"> + + <TextView + android:id="@android:id/title" + style="@style/PermissionSelectorWithWidgetPreferenceTitleStyle" /> + + <LinearLayout + android:id="@+id/summary_container" + style="@style/PermissionSelectorWithWidgetPreferenceSummaryContainerStyle"> + + <TextView + android:id="@android:id/summary" + style="@style/PermissionSelectorWithWidgetPreferenceSummaryStyle" /> + + <TextView + android:id="@+id/appendix" + style="@style/PermissionSelectorWithWidgetPreferenceAppendixStyle" /> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:id="@+id/selector_extra_widget_container" + style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle"> + + <View style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle" /> + + <ImageView + android:id="@+id/selector_extra_widget" + android:contentDescription="@string/settings_label" + style="@style/PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle" /> + </LinearLayout> +</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout> diff --git a/PermissionController/res/layout-v35/permission_preference_two_target.xml b/PermissionController/res/layout-v35/permission_preference_two_target.xml new file mode 100644 index 000000000..906393b3c --- /dev/null +++ b/PermissionController/res/layout-v35/permission_preference_two_target.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<!-- A direct copy of the following layout, but with overlayable styles: + ~ SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml + --> + +<!-- Based off preference_material_settings.xml except that ripple on only on the left side. --> +<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/PermissionTwoTargetPreferenceRootLayoutStyle"> + + <LinearLayout + android:id="@+id/icon_frame" + style="@style/PermissionTwoTargetPreferenceIconFrameStyle"> + + <androidx.preference.internal.PreferenceImageView + android:id="@android:id/icon" + style="@style/PermissionTwoTargetPreferenceIconStyle"/> + + </LinearLayout> + + <RelativeLayout style="@style/PermissionTwoTargetPreferenceTextContainerStyle"> + + <TextView + android:id="@android:id/title" + style="@style/PermissionTwoTargetPreferenceTitleStyle"/> + + <TextView + android:id="@android:id/summary" + style="@style/PermissionTwoTargetPreferenceSummaryStyle"/> + + </RelativeLayout> + + <LinearLayout + android:id="@+id/two_target_divider" + style="@style/PermissionTwoTargetPreferenceDividerContainerStyle"> + <View style="@style/PermissionTwoTargetPreferenceDividerStyle" /> + </LinearLayout> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout + android:id="@android:id/widget_frame" + style="@style/PermissionTwoTargetPreferenceWidgetFrameStyle" /> + +</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout> diff --git a/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml b/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml new file mode 100644 index 000000000..10e311110 --- /dev/null +++ b/PermissionController/res/layout-v35/permission_preference_widget_radiobutton.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<!-- A direct copy of the following layout, but with overlayable styles: + ~ SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.xml + --> +<RadioButton + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/checkbox" + style="@style/PermissionSelectorWithWidgetPreferenceWidgetRadioButton" /> diff --git a/PermissionController/res/layout/app_permission.xml b/PermissionController/res/layout/app_permission.xml index 67ac627fc..07a9f2987 100644 --- a/PermissionController/res/layout/app_permission.xml +++ b/PermissionController/res/layout/app_permission.xml @@ -114,6 +114,7 @@ <RadioButton android:id="@+id/select_radio_button" + android:layout_toStartOf="@id/edit_photos_divider" android:text="@string/app_permission_button_allow_limited_access" android:layout_alignParentStart="true" style="@style/AppPermissionRadioButton" diff --git a/PermissionController/res/layout/auto_grant_permissions_material3.xml b/PermissionController/res/layout/auto_grant_permissions_material3.xml new file mode 100644 index 000000000..d04a92865 --- /dev/null +++ b/PermissionController/res/layout/auto_grant_permissions_material3.xml @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- + ~ A lot of content in this file is identical to grant_permissions.xml + ~ Be sure to update both files when making changes. + --> + +<!-- In (hopefully very rare) case dialog is too high: allow scrolling --> +<!-- The following layout file is a fork of the phone layout version, customized for auto --> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/AutoPermissionGrantScrollView" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/grant_singleton" + style="@style/AutoPermissionGrantSingleton" + android:focusable="false" + android:importantForAccessibility="no"> + + <!-- The dialog --> + <LinearLayout + android:id="@+id/grant_dialog" + style="@style/AutoPermissionGrantDialogMaterial3" + android:focusable="false" + android:importantForAccessibility="no" + android:theme="@style/Theme.PermissionGrantDialog"> + + <LinearLayout + android:id="@+id/content_container" + style="@style/AutoPermissionGrantContent"> + + <LinearLayout style="@style/AutoPermissionGrantDescription"> + + <TextView + android:id="@+id/permission_message" + style="@style/AutoPermissionGrantTitleMessage" + android:accessibilityHeading="true" /> + + </LinearLayout> + + <TextView + android:id="@+id/detail_message" + style="@style/AutoPermissionGrantDetailMessage" /> + + </LinearLayout> + + <!-- location (precise/approximate) animations --> + <LinearLayout + android:id="@+id/permission_location_accuracy" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/permission_location_accuracy_fine_only" + style="@style/AutoPermissionLocationAccuracyImageViewMaterial13" + android:contentDescription="@string/precise_image_description" /> + + <ImageView + android:id="@+id/permission_location_accuracy_coarse_only" + style="@style/AutoPermissionLocationAccuracyImageViewMaterial13" + android:contentDescription="@string/approximate_image_description" /> + + <RadioGroup + android:id="@+id/permission_location_accuracy_radio_group" + style="@style/AutoPermissionLocationAccuracyRadioGroupMaterial3"> + + <RadioButton + android:id="@+id/permission_location_accuracy_radio_fine" + style="@style/AutoPermissionLocationAccuracyRadioFine" + android:text="@string/permgrouprequest_finelocation_imagetext" /> + + <RadioButton + android:id="@+id/permission_location_accuracy_radio_coarse" + style="@style/AutoPermissionLocationAccuracyRadioCoarse" + android:text="@string/permgrouprequest_coarselocation_imagetext" /> + </RadioGroup> + + </LinearLayout> + + <!-- Buttons on bottom of dialog --> + <LinearLayout style="@style/AutoPermissionGrantButtonListMaterial3"> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_dont_allow_more_selected_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_dont_select_more" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_no_upgrade_one_time_and_dont_ask_again_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_no_upgrade_one_time" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_no_upgrade_one_time_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_no_upgrade_one_time" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_no_upgrade_and_dont_ask_again_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_no_upgrade" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_no_upgrade_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_no_upgrade" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_deny_and_dont_ask_again_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_deny" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_deny_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_deny" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_selected_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_allow_limited_access" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_one_time_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_allow_one_time" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_foreground_only_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_allow_foreground" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_allow" /> + + <com.android.permissioncontroller.permission.ui.widget.SecureButton + android:id="@+id/permission_allow_all_button" + style="@style/AutoPermissionGrantButton" + android:text="@string/grant_dialog_button_allow_all" /> + + </LinearLayout> + + <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginBottom="20dp" + android:layout_marginTop="0dp" /> + </LinearLayout> + </LinearLayout> +</ScrollView> diff --git a/PermissionController/res/layout/car_switch_button_preference.xml b/PermissionController/res/layout/car_switch_button_preference.xml new file mode 100644 index 000000000..a2c2dcd8f --- /dev/null +++ b/PermissionController/res/layout/car_switch_button_preference.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:minHeight="?android:attr/listPreferredItemHeightSmall"> + + <FrameLayout + android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="?android:attr/listPreferredItemHeightSmall" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@android:id/title" + style="@style/AppPermissionLocationAccuracyTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:paddingBottom="@dimen/car_preference_row_vertical_margin" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingTop="@dimen/car_preference_row_vertical_margin" + android:singleLine="true" + android:text="@string/app_permission_location_accuracy" + android:textAppearance="?android:attr/textAppearanceLarge" + app:layout_constraintEnd_toStartOf="@android:id/widget_frame" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_default="spread" /> + + <TextView + android:id="@android:id/summary" + style="@style/AppPermissionLocationAccuracySubtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/car_preference_row_vertical_margin" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:text="@string/app_permission_location_accuracy_subtitle" + android:textAppearance="?android:attr/textAppearanceSmall" + app:layout_constraintEnd_toStartOf="@android:id/widget_frame" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@android:id/title" + app:layout_constraintWidth_default="spread" /> + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout/permission_footer_preference.xml b/PermissionController/res/layout/permission_footer_preference.xml new file mode 100644 index 000000000..52c1f76e1 --- /dev/null +++ b/PermissionController/res/layout/permission_footer_preference.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<!-- A direct copy of the SettingsLib FooterPreference/res/layout-v33/preference_footer.xml + ~ with overlayable styles + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/PermissionFooterPreferenceRootLayoutStyle"> + + <LinearLayout + android:id="@+id/icon_frame" + style="@style/PermissionFooterPreferenceIconFrameStyle"> + <ImageView + android:id="@android:id/icon" + style="@style/PermissionFooterPreferenceIconStyle"/> + </LinearLayout> + + <LinearLayout + style="@style/PermissionFooterPreferenceLearnMoreTextLayoutStyle"> + <TextView + android:id="@android:id/title" + style="@style/PermissionFooterPreferenceTitleTextStyle"/> + + <com.android.settingslib.widget.LinkTextView + android:id="@+id/settingslib_learn_more" + android:text="@string/settingslib_learn_more_text" + style="@style/PermissionFooterPreferenceLearnMoreTextStyle"/> + </LinearLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout/permission_preference.xml b/PermissionController/res/layout/permission_preference.xml new file mode 100644 index 000000000..314752140 --- /dev/null +++ b/PermissionController/res/layout/permission_preference.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- A direct copy of the SettingsLib SettingsTheme/res/layout-v33/settingslib_preference.xml, + ~ SettingsLib/SettingsTheme/res/layout-v31/settingslib_icon_frame.xml + ~ with overlayable styles + --> +<com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/PermissionPreferenceRootLayoutStyle"> + + <LinearLayout + android:id="@+id/icon_frame" + style="@style/PermissionPreferenceIconFrameStyle"> + + <androidx.preference.internal.PreferenceImageView + android:id="@android:id/icon" + style="@style/PermissionPreferenceIconStyle"/> + + </LinearLayout> + + <RelativeLayout + style="@style/PermissionPreferenceTextRelativeLayoutStyle"> + + <TextView + android:id="@android:id/title" + style="@style/PermissionPreferenceTitleTextStyle"/> + + <TextView + android:id="@android:id/summary" + style="@style/PermissionPreferenceSummaryTextStyle"/> + + </RelativeLayout> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout + android:id="@android:id/widget_frame" + style="@style/PermissionPreferenceWidgetFrameLayoutStyle"/> + +</com.android.permissioncontroller.permission.ui.handheld.v35.DrawableStateLinearLayout> diff --git a/PermissionController/res/layout/permission_preference_category.xml b/PermissionController/res/layout/permission_preference_category.xml new file mode 100644 index 000000000..5e80f90a5 --- /dev/null +++ b/PermissionController/res/layout/permission_preference_category.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- A direct copy of the AndroidX preference/preference/res/layout/preference_category_material.xml + ~ with overlayable styles + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/PermissionPreferenceCategoryRootLayoutStyle"> + + <RelativeLayout + style="@style/PermissionPreferenceCategoryTextRelativeLayoutStyle"> + + <TextView + android:id="@android:id/title" + style="@style/PermissionPreferenceCategoryTitleTextStyle"/> + + <TextView + android:id="@android:id/summary" + style="@style/PermissionPreferenceCategorySummaryTextStyle"/> + + </RelativeLayout> +</LinearLayout>
\ No newline at end of file diff --git a/PermissionController/res/layout/preference_permissions_switch.xml b/PermissionController/res/layout/preference_permissions_switch_television.xml index 85ded739d..7168b7a0a 100644 --- a/PermissionController/res/layout/preference_permissions_switch.xml +++ b/PermissionController/res/layout/preference_permissions_switch_television.xml @@ -39,8 +39,8 @@ android:paddingBottom="4dp"> <androidx.preference.internal.PreferenceImageView android:id="@android:id/icon" - android:layout_width="@dimen/permission_icon_size" - android:layout_height="@dimen/permission_icon_size" + android:layout_width="@dimen/tv_permission_icon_size" + android:layout_height="@dimen/tv_permission_icon_size" android:scaleType="fitCenter" /> </LinearLayout> diff --git a/PermissionController/res/layout/preference_permissions.xml b/PermissionController/res/layout/preference_permissions_television.xml index b40a0e8fe..53a8431c6 100644 --- a/PermissionController/res/layout/preference_permissions.xml +++ b/PermissionController/res/layout/preference_permissions_television.xml @@ -39,8 +39,8 @@ android:paddingBottom="4dp"> <androidx.preference.internal.PreferenceImageView android:id="@android:id/icon" - android:layout_width="@dimen/permission_icon_size" - android:layout_height="@dimen/permission_icon_size" + android:layout_width="@dimen/tv_permission_icon_size" + android:layout_height="@dimen/tv_permission_icon_size" android:scaleType="fitCenter" /> </LinearLayout> diff --git a/PermissionController/res/layout/radio_button_preference_widget.xml b/PermissionController/res/layout/radio_button_preference_widget.xml index cd2a2ce42..9bff4e46e 100644 --- a/PermissionController/res/layout/radio_button_preference_widget.xml +++ b/PermissionController/res/layout/radio_button_preference_widget.xml @@ -19,7 +19,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:minWidth="56dp" + android:minWidth="@dimen/toggle_preference_container_min_width" android:layout_height="match_parent"> <RadioButton diff --git a/PermissionController/res/layout/switch_button_preference_widget.xml b/PermissionController/res/layout/switch_button_preference_widget.xml new file mode 100644 index 000000000..f5a2d595b --- /dev/null +++ b/PermissionController/res/layout/switch_button_preference_widget.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:minWidth="@dimen/toggle_preference_container_min_width" + android:layout_height="match_parent"> + + <Switch + android:id="@+id/location_accuracy_switch" + android:checked="false" + android:duplicateParentState="true" + style="@style/AppPermissionLocationAccuracySwitch" /> +</FrameLayout> diff --git a/PermissionController/res/layout/footer_preference.xml b/PermissionController/res/layout/unused_apps_footer_preference.xml index 157d97d3b..157d97d3b 100644 --- a/PermissionController/res/layout/footer_preference.xml +++ b/PermissionController/res/layout/unused_apps_footer_preference.xml diff --git a/PermissionController/res/values-af-television/strings.xml b/PermissionController/res/values-af-television/strings.xml index 26a8bec01..fe101523c 100644 --- a/PermissionController/res/values-af-television/strings.xml +++ b/PermissionController/res/values-af-television/strings.xml @@ -20,8 +20,8 @@ <string name="grant_dialog_how_to_change" msgid="997462845048160559">"Jy kan dit later verander in Instellings > Programme"</string> <string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> <string name="preference_show_system_apps" msgid="4262140518693221093">"Wys stelselprogramme"</string> - <string name="app_permissions_decor_title" msgid="7438716722786036814">"Programtoestemmings"</string> - <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Programtoestemmings"</string> + <string name="app_permissions_decor_title" msgid="7438716722786036814">"Apptoestemmings"</string> + <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Apptoestemmings"</string> <string name="permission_apps_decor_title" msgid="2811550489429789828">"<xliff:g id="PERMISSION">%1$s</xliff:g>-toestemmings"</string> <string name="additional_permissions_decor_title" msgid="5113847982502484225">"Bykomende toestemmings"</string> <string name="system_apps_decor_title" msgid="4402004958937474803">"<xliff:g id="PERMISSION">%1$s</xliff:g>-toestemmings"</string> diff --git a/PermissionController/res/values-af-v33/strings.xml b/PermissionController/res/values-af-v33/strings.xml index 6d6a118cd..8bfc6821d 100644 --- a/PermissionController/res/values-af-v33/strings.xml +++ b/PermissionController/res/values-af-v33/strings.xml @@ -16,8 +16,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="role_dialer_request_description" msgid="6188305064871543419">"Hierdie program sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string> - <string name="role_sms_request_description" msgid="1506966389698625395">"Hierdie program sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang kry tot jou kamera, kontakte, lêers, mikrofoon, foon en SMS\'e"</string> + <string name="role_dialer_request_description" msgid="6188305064871543419">"Hierdie app sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string> + <string name="role_sms_request_description" msgid="1506966389698625395">"Hierdie app sal toegelaat word om vir jou kennisgewings te stuur, en sal toegang kry tot jou kamera, kontakte, lêers, mikrofoon, foon en SMS\'e"</string> <string name="permission_description_summary_storage" msgid="1917071243213043858">"Programme met hierdie toestemming het toegang tot alle lêers op hierdie toestel"</string> <string name="work_policy_title" msgid="832967780713677409">"Jou werkbeleidinligting"</string> <string name="work_policy_summary" msgid="3886113358084963931">"Instellings wat deur jou IT-admin bestuur word"</string> diff --git a/PermissionController/res/values-af-v35/strings.xml b/PermissionController/res/values-af-v35/strings.xml new file mode 100644 index 000000000..ae1626d6d --- /dev/null +++ b/PermissionController/res/values-af-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sekuriteit"</string> +</resources> diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml index 75b3cf873..44b10ebe6 100644 --- a/PermissionController/res/values-af/strings.xml +++ b/PermissionController/res/values-af/strings.xml @@ -21,15 +21,16 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"toestemmings"</string> <string name="cancel" msgid="8943320028373963831">"Kanselleer"</string> <string name="back" msgid="6249950659061523680">"Terug"</string> + <string name="dialog_close" msgid="6840699812532384661">"Maak toe"</string> <string name="available" msgid="6007778121920339498">"Beskikbaar"</string> <string name="blocked" msgid="9195547604866033708">"Geblokkeer"</string> <string name="on" msgid="280241003226755921">"Aan"</string> <string name="off" msgid="1438489226422866263">"Af"</string> <string name="uninstall_or_disable" msgid="4496612999740858933">"Deïnstalleer of deaktiveer"</string> - <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Program nie gevind nie"</string> + <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App nie gevind nie"</string> <string name="grant_dialog_button_deny" msgid="88262611492697192">"Moenie toelaat nie"</string> <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Moenie toelaat nie en moenie weer vra nie"</string> - <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die program gebruik word\""</string> + <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die app 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" msgid="5939066403732409516">"Laat alles toe"</string> @@ -43,7 +44,7 @@ <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> <string name="permission_warning_template" msgid="2247087781222679458">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string> <string name="permission_add_background_warning_template" msgid="1812914855915092273">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> altyd toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string> - <string name="allow_permission_foreground_only" msgid="116465816039675404">"Net terwyl program gebruik word"</string> + <string name="allow_permission_foreground_only" msgid="116465816039675404">"Net terwyl app gebruik word"</string> <string name="allow_permission_always" msgid="5194342531206054051">"Altyd"</string> <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Moenie toelaat nie en moenie weer vra nie"</string> <string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> is gedeaktiveer"</string> @@ -79,9 +80,9 @@ <string name="additional_permissions" msgid="5801285469338873430">"Bykomende toestemmings"</string> <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Maak programinligting oop"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{Nog #}other{Nog #}}"</string> - <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit dalk nie meer soos bedoel werk nie."</string> - <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming toelaat, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) toegelaat word."</string> - <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming weier, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) geweier word."</string> + <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit dalk nie meer soos bedoel werk nie."</string> + <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming toelaat, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) toegelaat word."</string> + <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy hierdie toestemming weier, sal toegang tot alle berging (insluitend foto\'s, video\'s, musiek, oudio en ander lêers) geweier word."</string> <string name="default_permission_description" msgid="4624464917726285203">"voer \'n onbekende handeling uit"</string> <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> van <xliff:g id="COUNT_1">%2$d</xliff:g> programme toegelaat"</string> <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> programme toegelaat"</string> @@ -94,9 +95,9 @@ <string name="location_settings" msgid="3624412509133422562">"Ligginginstellings"</string> <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> is \'n verskaffer van liggingdienste vir hierdie toestel. Liggingtoegang kan vanuit ligginginstellings gewysig word."</string> <string name="system_warning" msgid="1173400963234358816">"As jy hierdie toestemming weier, sal basiese kenmerke van jou toestel dalk nie meer soos bedoel werk nie."</string> - <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie program toegang tot foto\'s en video\'s gee nie, sal dit ook nie toegang tot musiek en ander oudio hê nie."</string> - <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie program toegang tot musiek en ander oudio gee nie, sal dit ook nie toegang tot foto\'s en video\'s hê nie."</string> - <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"As jy hierdie toestemming weier, sal sommige kenmerke van jou toestel wat deur hierdie program bestuur word dalk nie meer soos bedoel werk nie."</string> + <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie app toegang tot foto\'s en video\'s gee nie, sal dit ook nie toegang tot musiek en ander oudio hê nie."</string> + <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Hierdie app is vir \'n ouer weergawe van Android ontwerp. As jy nie vir hierdie app toegang tot musiek en ander oudio gee nie, sal dit ook nie toegang tot foto\'s en video\'s hê nie."</string> + <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"As jy hierdie toestemming weier, sal sommige kenmerke van jou toestel wat deur hierdie app bestuur word dalk nie meer soos bedoel werk nie."</string> <string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Afgedwing deur beleid"</string> <string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"Agtergrondtoegang is gedeaktiveer volgens beleid"</string> <string name="permission_summary_enabled_by_policy_background_only" msgid="8287675974767104279">"Agtergrondtoegang is geaktiveer volgens beleid"</string> @@ -117,12 +118,12 @@ <string name="other_permissions" msgid="2901186127193849594">"Ander programvermoëns"</string> <string name="permission_request_title" msgid="8790310151025020126">"Toestemmingsversoek"</string> <string name="permission_review_title_template_install" msgid="1284337937156289081">"Kies waartoe <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang mag kry"</string> - <string name="permission_review_title_template_update" msgid="3232333580548588657">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> is opgedateer. Kies waartoe hierdie program toegang mag kry."</string> + <string name="permission_review_title_template_update" msgid="3232333580548588657">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> is opgedateer. Kies waartoe hierdie app toegang mag kry."</string> <string name="review_button_cancel" msgid="2191147944056548886">"Kanselleer"</string> <string name="review_button_continue" msgid="2527918375047602199">"Gaan voort"</string> <string name="new_permissions_category" msgid="552995090178417611">"Nuwe toestemmings"</string> <string name="current_permissions_category" msgid="4292990083585728880">"Huidige toestemmings"</string> - <string name="message_staging" msgid="9110563899955511866">"Voer tans program uit …"</string> + <string name="message_staging" msgid="9110563899955511866">"Voer tans app uit …"</string> <string name="app_name_unknown" msgid="1319665005754048952">"Onbekend"</string> <string name="permission_usage_title" msgid="1568233336351734538">"Privaatheidkontroleskerm"</string> <string name="auto_permission_usage_summary" msgid="7335667266743337075">"Bekyk watter programme onlangs toestemmings gebruik het"</string> @@ -132,7 +133,7 @@ <string name="perm_usage_adv_info_summary_more_items" msgid="949055326299562218">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g> en nog <xliff:g id="NUM">%3$s</xliff:g>"</string> <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Tydlyn van wanneer programme jou <xliff:g id="PERMGROUP">%1$s</xliff:g> in die afgelope 24 uur gebruik het"</string> <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Tydlyn van wanneer programme jou <xliff:g id="PERMGROUP">%1$s</xliff:g> in die afgelope 7 dae gebruik het"</string> - <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Wanneer hierdie program jou <xliff:g id="PERMGROUP">%1$s</xliff:g>-toestemming gebruik het"</string> + <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Wanneer hierdie app jou <xliff:g id="PERMGROUP">%1$s</xliff:g>-toestemming gebruik het"</string> <string name="permission_usage_access_dialog_learn_more" msgid="7121468469493184613">"Kom meer te wete"</string> <string name="learn_more_content_description" msgid="8673699744544502539">"Kom meer te wete oor <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> <string name="manage_permission_summary" msgid="4117555482684114317">"Beheer apptoegang tot jou <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> @@ -203,17 +204,17 @@ <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Sien alle apps met hierdie toestemming"</string> <string name="assistant_mic_label" msgid="1011432357152323896">"Wys Assistent-mikrofoongebruik"</string> <string name="unused_apps_category_title" msgid="2988455616845243901">"Instellings vir ongebruikte apps"</string> - <string name="auto_revoke_label" msgid="5068393642936571656">"Verwyder toestemmings as program nie gebruik word nie"</string> + <string name="auto_revoke_label" msgid="5068393642936571656">"Verwyder toestemmings as app nie gebruik word nie"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Verwyder toestemmings en maak spasie beskikbaar"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"Onderbreek appaktiwiteit as ongebruik"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Bestuur app indien ongebruik"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Verwyder toestemmings, vee tydelike lêers uit, en stop kennisgewings"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Verwyder toestemmings, vee tydelike lêers uit, stop kennisgewings en argiveer die app"</string> - <string name="auto_revoke_summary" msgid="5867548789805911683">"Om jou data te beskerm, sal toestemmings vir hierdie program verwyder word as die program \'n paar maande nie gebruik word nie."</string> + <string name="auto_revoke_summary" msgid="5867548789805911683">"Om jou data te beskerm, sal toestemmings vir hierdie app verwyder word as die app \'n paar maande nie gebruik word nie."</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Om jou data te beskerm, sal die volgende toestemmings verwyder word as dit vir \'n paar maande nie gebruik word nie: <xliff:g id="PERMS">%1$s</xliff:g>."</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Om jou data te beskerm, is toestemmings verwyder van programme wat jy \'n paar maande gelede laas gebruik het."</string> - <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Maak die program oop as jy toestemmings weer wil toelaat."</string> - <string name="auto_revoke_disabled" msgid="8697684442991567188">"Outomatiese verwydering is tans gedeaktiveer vir hierdie program."</string> + <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Maak die app oop as jy toestemmings weer wil toelaat."</string> + <string name="auto_revoke_disabled" msgid="8697684442991567188">"Outomatiese verwydering is tans gedeaktiveer vir hierdie app."</string> <string name="auto_revocable_permissions_none" msgid="8334929619113991466">"Geen toestemmings wat outomaties herroep kan word, is tans verleen nie"</string> <string name="auto_revocable_permissions_one" msgid="5299112369449458176">"<xliff:g id="PERM">%1$s</xliff:g> toestemming sal verwyder word."</string> <string name="auto_revocable_permissions_two" msgid="4874067408752041716">"<xliff:g id="PERM_0">%1$s</xliff:g> en <xliff:g id="PERM_1">%2$s</xliff:g> toestemmings sal verwyder word."</string> @@ -226,10 +227,10 @@ <string name="unused_apps_page_summary" msgid="1867593913217272155">"As \'n app vir \'n paar maande nie gebruik word nie:\n\n• Word toestemmings verwyder om jou privaatheid te beskerm\n• Word kennisgewings gestop om batterykrag te bespaar\n• Word tydelike lêers verwyder om spasie beskikbaar te maak\n\nMaak die app oop om weer toestemmings en kennisgewings toe te laat."</string> <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"As ’n app ’n maand lank nie gebruik is nie:\n\n• Toestemmings word verwyder om jou data te beskerm\n• Tydelike lêers word verwyder om spasie beskikbaar te maak\n\nMaak die app oop om weer toestemmings te gee."</string> <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Meer as # maand gelede laas oopgemaak}other{Meer as # maande gelede laas oopgemaak}}"</string> - <string name="last_opened_summary" msgid="5248984030024968808">"Program is <xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string> + <string name="last_opened_summary" msgid="5248984030024968808">"App is <xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string> <string name="last_opened_summary_short" msgid="1646067226191176825">"<xliff:g id="DATE">%s</xliff:g> laas oopgemaak"</string> - <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"As jy toelaat dat alle lêers bestuur word, kan hierdie program enige lêers in gedeelde bergingspasie op hierdie toestel en gekoppelde bergingtoestelle kry, wysig en uitvee. Die program kan toegang tot lêers kry sonder om jou te vra."</string> - <string name="special_file_access_dialog" msgid="583804114020740610">"Laat hierdie program toe om lêers op die toestel en enige gekoppelde bergingtoestelle te kry, te wysig en uit te vee? Hierdie program kan toegang tot lêers kry sonder om jou te vra."</string> + <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"As jy toelaat dat alle lêers bestuur word, kan hierdie app enige lêers in gedeelde bergingspasie op hierdie toestel en gekoppelde bergingtoestelle kry, wysig en uitvee. Die app kan toegang tot lêers kry sonder om jou te vra."</string> + <string name="special_file_access_dialog" msgid="583804114020740610">"Laat hierdie app toe om lêers op die toestel en enige gekoppelde bergingtoestelle te kry, te wysig en uit te vee? Hierdie app kan toegang tot lêers kry sonder om jou te vra."</string> <string name="permission_description_summary_generic" msgid="5401399408814903391">"Apps met hierdie toestemming <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string> <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Programme met hierdie toestemming kan ingaan by fisieke aktiwiteit, soos stap, fietsry, ry, treëtelling, en meer"</string> <string name="permission_description_summary_calendar" msgid="103329982944411010">"Programme met hierdie toestemming kan toegang tot jou kalender kry"</string> @@ -263,12 +264,12 @@ <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_one" msgid="6690347469376854137">"1 ongebruikte app"</string> <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> ongebruikte programme"</string> <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Toestemmings is verwyder om jou privaatheid te beskerm. Tik om na te gaan"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Toestemmings vir ongebruikte programme verwyder"</string> <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Sommige programme is \'n paar maande laas gebruik. Tik om te na te gaan."</string> - <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ongebruikte program}other{# ongebruikte programme}}"</string> + <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# ongebruikte app}other{# ongebruikte apps}}"</string> <string name="unused_apps_notification_content" msgid="9195026773244581246">"Toestemmings en tydelike lêers is verwyder en kennisgewings is gestop. Tik om na te gaan."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Gaan programme na waarvoor toestemmings verwyder is"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Toestemmings en tydelike lêers is verwyder en kennisgewings is gestop vir programme wat jy ’n ruk lank nie gebruik het nie."</string> @@ -277,7 +278,7 @@ <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Terwyl jy bestuur het, het jy <xliff:g id="APP">%1$s</xliff:g> toegang gegee tot <xliff:g id="PERMISSION">%2$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Terwyl jy bestuur het, het jy <xliff:g id="APP">%1$s</xliff:g> toegang gegee tot <xliff:g id="PERMISSION_1">%2$s</xliff:g> en <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Terwyl jy bestuur het, het jy <xliff:g id="COUNT">%1$d</xliff:g> toestemmings aan <xliff:g id="APP">%2$s</xliff:g> gegee"</string> - <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Terwyl jy bestuur het, het jy <xliff:g id="APP_0">%1$s</xliff:g> en # ander program toegang gegee}other{Terwyl jy bestuur het, het jy <xliff:g id="APP_1">%1$s</xliff:g> en # ander programme toegang gegee}}"</string> + <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Terwyl jy bestuur het, het jy <xliff:g id="APP_0">%1$s</xliff:g> en # ander app toegang gegee}other{Terwyl jy bestuur het, het jy <xliff:g id="APP_1">%1$s</xliff:g> en # ander apps toegang gegee}}"</string> <string name="go_to_settings" msgid="1053735612211228335">"Gaan na Instellings"</string> <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Sommige programme is \'n paar maande laas gebruik"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"Toestemmings wat verwyder is"</string> @@ -286,23 +287,23 @@ <string name="months_ago" msgid="1766026492610646354">"<xliff:g id="COUNT">%1$d</xliff:g> maande gelede"</string> <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"Toestemmings is verwyder om jou privaatheid te beskerm"</string> <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> het jou ligging op die agtergrond gekry"</string> - <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Hierdie program het altyd toegang tot jou ligging. Tik om te verander."</string> - <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Gaan program met toegang tot jou kennisgewings na"</string> + <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Hierdie app het altyd toegang tot jou ligging. Tik om te verander."</string> + <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Gaan app met toegang tot jou kennisgewings na"</string> <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> kan inhoud in jou kennisgewings toemaak, daarop reageer en toegang daartoe kry"</string> - <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Hierdie program kan inhoud in jou kennisgewings toemaak, daarop reageer en toegang daartoe kry. Sommige programme het hierdie toegang nodig om te werk soos hulle moet."</string> + <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Hierdie app kan inhoud in jou kennisgewings toemaak, daarop reageer en toegang daartoe kry. Sommige apps het hierdie toegang nodig om te werk soos hulle moet."</string> <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Verwyder toegang"</string> <string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"Sien meer opsies"</string> <string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"Toegang is verwyder"</string> - <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Gaan program met volle toesteltoegang na"</string> + <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Gaan app met volle toesteltoegang na"</string> <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> kan jou skerm sien en handelinge op jou toestel uitvoer. Toeganklikheidprogramme het hierdie soort toegang nodig om te werk soos hulle moet."</string> - <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Hierdie program kan jou skerm sien en handelinge op jou toestel uitvoer. Toeganklikheidprogramme het hierdie soort toegang nodig om te werk soos hulle moet, maar gaan die program na en maak seker dat jy dit kan vertrou."</string> + <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Hierdie app kan jou skerm sien en handelinge op jou toestel uitvoer. Toeganklikheidapps het hierdie soort toegang nodig om te werk soos hulle moet, maar gaan die app na en maak seker dat jy dit kan vertrou."</string> <string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Verwyder toegang"</string> - <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Bekyk program met volle toegang"</string> + <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Bekyk apps met volle toegang"</string> <string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"Toegang is verwyder"</string> <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android-stelsel"</string> <string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"Programtoestemmings is verwyder om privaatheid te beskerm"</string> <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string> - <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> en 1 ander program is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string> + <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> en 1 ander app is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string> <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> en <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> ander programme is vir \'n paar maande nie gebruik nie. Tik om te kontroleer."</string> <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"1 app is ongebruik"</string> <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> programme is ongebruik"</string> @@ -314,9 +315,9 @@ <string name="unused_permissions_subtitle_many" msgid="4387289202207450238">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g>, <xliff:g id="PERM_NAME_1">%2$s</xliff:g> en nog <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g>"</string> <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Toestemmings is verwyder uit programme wat jy vir \'n paar maande nie gebruik het nie om jou data te beskerm"</string> <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Om jou data te beskerm, is toestemmings verwyder uit sekere programme wat jy \'n paar maande gelede laas gebruik het"</string> - <string name="one_unused_app_summary" msgid="7831913934488881991">"1 program is \'n paar maande gelede laas gebruik"</string> + <string name="one_unused_app_summary" msgid="7831913934488881991">"1 app is \'n paar maande gelede laas gebruik"</string> <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> programme is \'n paar maande gelede laas gebruik."</string> - <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Net terwyl die program gebruik word"</string> + <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Net terwyl die app gebruik word"</string> <string name="permission_subtitle_media_only" msgid="8917869683764720717">"Media"</string> <string name="permission_subtitle_all_files" msgid="4982613338298067862">"Alle lêers"</string> <string name="permission_subtitle_background" msgid="8916750995309083180">"Altyd toegelaat"</string> @@ -364,13 +365,13 @@ <string name="role_dialer_short_label" msgid="7186888549465352489">"Foonapp"</string> <string name="role_dialer_description" msgid="8768708633696539612">"Apps wat jou toelaat om telefoonoproepe op jou toestel te maak en te ontvang"</string> <string name="role_dialer_request_title" msgid="5959618560705912058">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekfoonprogram?"</string> - <string name="role_dialer_request_description" msgid="6288839625724909320">"Hierdie program sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string> + <string name="role_dialer_request_description" msgid="6288839625724909320">"Hierdie app sal toegang tot jou kamera, kontakte, mikrofoon, foon en SMS\'e kry"</string> <string name="role_dialer_search_keywords" msgid="3324448983559188087">"beller"</string> <string name="role_sms_label" msgid="8456999857547686640">"Verstek-SMS-app"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"SMS-app"</string> <string name="role_sms_description" msgid="3424020199148153513">"Apps wat jou toelaat om jou foonnommer te gebruik om kort SMS\'e, foto\'s, video\'s en meer te stuur en te ontvang"</string> - <string name="role_sms_request_title" msgid="7953552109601185602">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstek-SMS-program?"</string> - <string name="role_sms_request_description" msgid="2691004766132144886">"Hierdie program sal toegang tot jou kamera, kontakte, lêers en media, mikrofoon, foon en SMS\'e kry"</string> + <string name="role_sms_request_title" msgid="7953552109601185602">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstek-SMS-app?"</string> + <string name="role_sms_request_description" msgid="2691004766132144886">"Hierdie app sal toegang tot jou kamera, kontakte, lêers en media, mikrofoon, foon en SMS\'e kry"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"sms, teksboodskappe, boodskappe, boodskappe"</string> <string name="role_emergency_label" msgid="7028825857206842366">"Versteknoodprogram"</string> <string name="role_emergency_short_label" msgid="2388431453335350348">"Noodprogram"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Geen"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Stelselverstek)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Geen apps nie"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Ander NFC-dienste"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Gekies"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Gekies – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"spesiale apptoegang"</string> @@ -447,7 +449,7 @@ <string name="no_special_app_access" msgid="6950277571805106247">"Geen spesiale apptoegang nie"</string> <string name="special_app_access_no_apps" msgid="4102911722787886970">"Geen apps nie"</string> <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Steun nie werkprofiel nie"</string> - <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Let wel: As jy jou toestel herbegin en \'n skermslot is gestel, kan hierdie program nie begin totdat jy jou toestel ontsluit nie."</string> + <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Let wel: As jy jou toestel herbegin en \'n skermslot is gestel, kan hierdie app nie begin totdat jy jou toestel ontsluit nie."</string> <string name="assistant_confirmation_message" msgid="7476540402884416212">"Die assistent sal inligting oor programme wat tans op jou stelsel gebruik word, kan lees, insluitend inligting wat op jou skerm sigbaar is of toeganklik is binne die programme."</string> <string name="incident_report_channel_name" msgid="3144954065936288440">"Deel ontfoutingsdata"</string> <string name="incident_report_notification_title" msgid="4635984625656519773">"Deel gedetailleerde ontfoutingsdata?"</string> @@ -471,13 +473,13 @@ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou kontakte op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_location" msgid="6990232580121067883">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot hierdie toestel se ligging?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> se ligging?"</string> - <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die program sal net toegang tot die ligging hê terwyl jy die program gebruik"</string> + <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die app sal net toegang tot die ligging hê terwyl jy die app gebruik"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot hierdie toestel se ligging?"</string> <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> se ligging?"</string> - <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Hierdie program wil dalk die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Hierdie app wil dalk die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Verander liggingtoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Verander liggingtoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Hierdie program wil die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Hierdie app wil die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal?"</string> <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestelle in die omtrek op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> soek, aan hulle koppel, en hul relatiewe posisie bepaal?"</string> <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal? "<annotation id="link">"Laat toe in Instellings."</annotation></string> @@ -503,35 +505,35 @@ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot meer foto’s en video’s op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om oudio op te neem op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die program sal net kan oudio opneem terwyl jy die program gebruik"</string> + <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die app sal net kan oudio opneem terwyl jy die app 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> <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om oudio op te neem op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Hierdie program wil dalk die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Hierdie app wil dalk die hele tyd oudio opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Verander mikrofoontoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Verander mikrofoontoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Hierdie program wil die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Hierdie app wil die hele tyd oudio opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou fisieke aktiwiteit?"</string> <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou fisieke aktiwiteit op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_camera" msgid="5123097035410002594">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om foto\'s te neem en video\'s op te neem?"</string> <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om foto’s te neem en video’s op te neem op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Die program sal net kan foto\'s neem en video\'s opneem terwyl jy die program gebruik"</string> + <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Die app sal net kan foto\'s neem en video\'s opneem terwyl jy die app gebruik"</string> <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om foto\'s te neem en video\'s op te neem?"</string> <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om foto’s te neem en video’s op te neem op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Hierdie program wil dalk die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Hierdie app wil dalk die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Verander kameratoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Verander kameratoegang vir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Hierdie program wil die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> + <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Hierdie app wil die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string> <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou foonoproeprekords?"</string> <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou foon se oproeprekords op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_phone" msgid="1829234136997316752">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om foonoproepe te maak en te bestuur?"</string> <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om foonoproepe te maak en te bestuur op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot sensordata oor jou lewenstekens?"</string> <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot sensordata oor jou lewenstekens op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Dié program wil dalk deurentyd toegang tot sensordata oor jou lewenstekens hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Gaan na instellings"</annotation>" om hierdie verandering te maak."</string> + <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Dié app wil dalk deurentyd toegang tot sensordata oor jou lewenstekens hê, selfs wanneer jy nie die app gebruik nie. "<annotation id="link">"Gaan na instellings"</annotation>" om hierdie verandering te maak."</string> <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot die sensordata oor jou lewenstekens?"</string> <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot die sensordata oor jou lewenstekens op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Gaan na instellings"</annotation>" om altyd vir hierdie program toegang tot liggaamsensordata te gee, selfs wanneer jy nie die program gebruik nie."</string> - <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> steeds toegang tot liggaamsensordata terwyl die program gebruik word?"</string> + <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Gaan na instellings"</annotation>" om altyd vir hierdie app toegang tot liggaamsensordata te gee, selfs wanneer jy nie die app gebruik nie."</string> + <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> steeds toegang tot liggaamsensordata terwyl die app gebruik word?"</string> <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> steeds toegang tot liggaamsensordata op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> terwyl die app gebruik word?"</string> <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Laat <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toe om vir jou kennisgewings te stuur?"</string> <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang om vir jou kennisgewings te stuur op <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> @@ -551,8 +553,8 @@ <string name="privdash_label_24h" msgid="1512532123865375319">"Afgelope\n24 uur"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"Afgelope\n7 dae"</string> <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> persent"</string> - <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op die statusbalk of jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string> - <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string> + <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie app se toestemminggebruik word nie op die statusbalk of jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string> + <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie app se toestemminggebruik word nie op jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"Toestelkamera is geblokkeer"</string> <string name="blocked_microphone_title" msgid="1631517143648232585">"Toestelmikrofoon is geblokkeer"</string> <string name="blocked_location_title" msgid="2005608279812892383">"Toestelligging is af"</string> @@ -586,8 +588,8 @@ <string name="permissions_removed_qs" msgid="8957319130625294572">"Toestemming is verwyder"</string> <string name="camera_usage_qs" msgid="4394233566086665994">"Sien onlangse kameragebruik"</string> <string name="microphone_usage_qs" msgid="8527666682168170417">"Sien onlangse mikrofoongebruik"</string> - <string name="remove_camera_qs" msgid="3649996161066883350">"Verwyder toestemming vir hierdie program"</string> - <string name="remove_microphone_qs" msgid="1276551965129953198">"Verwyder toestemming vir hierdie program"</string> + <string name="remove_camera_qs" msgid="3649996161066883350">"Verwyder toestemming vir hierdie app"</string> + <string name="remove_microphone_qs" msgid="1276551965129953198">"Verwyder toestemming vir hierdie app"</string> <string name="manage_service_qs" msgid="7862555549364153805">"Bestuur diens"</string> <string name="manage_permissions_qs" msgid="3780541819763475434">"Bestuur toestemmings"</string> <string name="active_call_usage_qs" msgid="8559974395932523391">"Word tans gebruik deur foonoproep"</string> @@ -608,16 +610,16 @@ <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Toegang tot foto\'s en video\'s sal ook nie toegelaat word"</string> <string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"Toegang tot musiek- en oudiolêers sal ook toegelaat word"</string> <string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"Toegang tot musiek- en oudiolêers sal ook nie toegelaat word nie"</string> - <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot musiek- en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry."</string> - <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry nie."</string> - <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot foto\'s en video\'s het, sal dit ook toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry."</string> - <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot foto\'s en video\'s het nie, sal dit ook nie toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry nie."</string> - <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot musiek en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s en video\'s te kry."</string> - <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string> - <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program toegang tot foto\'s en video\'s het, sal dit ook toegelaat word toegang tot musiek-, oudio- en ander lêers te kry."</string> - <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Hierdie program steun nie die jongste weergawe van Android nie. As hierdie program nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string> - <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Gaan program met agtergrondliggingtoegang na"</string> - <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> het altyd toegang tot jou ligging, selfs wanneer die program toe is"</string> + <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot musiek- en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry."</string> + <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s, video\'s en ander lêers te kry nie."</string> + <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot foto\'s en video\'s het, sal dit ook toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry."</string> + <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot foto\'s en video\'s het nie, sal dit ook nie toegelaat word om toegang tot musiek-, oudio- en ander lêers te kry nie."</string> + <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot musiek en oudiolêers het, sal dit ook toegelaat word om toegang tot foto\'s en video\'s te kry."</string> + <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string> + <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app toegang tot foto\'s en video\'s het, sal dit ook toegelaat word toegang tot musiek-, oudio- en ander lêers te kry."</string> + <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Hierdie app steun nie die jongste weergawe van Android nie. As hierdie app nie toegang tot musiek- en oudiolêers het nie, sal dit ook nie toegelaat word om toegang tot foto\'s en video\'s te kry nie."</string> + <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Gaan app met agtergrondliggingtoegang na"</string> + <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> het altyd toegang tot jou ligging, selfs wanneer die app toe is"</string> <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Gaan app met agtergrondliggingtoegang na"</string> <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Hierdie app het altyd toegang tot jou ligging, selfs wanneer dit toe is.\n\nSekere veiligheid- en noodapps moet op die agtergrond toegang tot jou ligging hê om te kan werk soos hulle moet."</string> <string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Toegang is verander"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Gister ingegaan <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ingegaan <xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jou eenmalige wagwoord is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Laat beperkte instellings toe"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Hierdie instelling is vir jou veiligheid tans onbeskikbaar."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handeling nie beskikbaar tydens ’n foonoproep nie"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Dit word nie toegelaat om tydens ’n foonoproep apps toe te laat om ander apps te installeer nie.\n\n Swendelaars versoek dikwels hierdie soort handeling tydens foonoproepgesprekke en dis dus geblokkeer om jou te beskerm. As jy aangesê word om hierdie handeling te doen deur iemand wat jy nie ken nie, kan dit ’n swendelary wees."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Toegang tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> is geweier vir die app"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die app het toegang tot toestemming vir sensitiewe inligting versoek wat jou persoonlike en finansiële inligting in gevaar kan stel.<xliff:g id="ID_1"><br><br></xliff:g>Dit is moontlik dat die app nie reg sal werk sonder hierdie beperkte toestemming nie. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Kry meer inligting oor hoe om toegang toe te laat</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Toegang is geweier vir app om die verstek <xliff:g id="ROLE_NAME">%1$s</xliff:g> te wees"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Toestemmingsversoek is onderdruk"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Hierdie app versoek bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou foon."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Vir noodoproep of -teksboodskap"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ligging is na nooddienste toe gestuur"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Hierdie app het toegang tot jou toestel se ligging gekry tydens ’n oproep of teksboodskap na ’n noodnommer. Dit kan gebeur selfs al het die app nie liggingtoestemming nie of al is die toestelligging af. "<a href="https://support.google.com/android/answer/9319337">"Meer inligting"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Kyk op jou foon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Kon nie jou foon bereik nie"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Herprobeer?"</string> </resources> diff --git a/PermissionController/res/values-am-v35/strings.xml b/PermissionController/res/values-am-v35/strings.xml new file mode 100644 index 000000000..3d2bdc7e9 --- /dev/null +++ b/PermissionController/res/values-am-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ደህንነት"</string> +</resources> diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml index 6602a268d..e1f87b92c 100644 --- a/PermissionController/res/values-am/strings.xml +++ b/PermissionController/res/values-am/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ፈቃዶች"</string> <string name="cancel" msgid="8943320028373963831">"ይቅር"</string> <string name="back" msgid="6249950659061523680">"ተመለስ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ዝጋ"</string> <string name="available" msgid="6007778121920339498">"ይገኛል"</string> <string name="blocked" msgid="9195547604866033708">"ታግዷል"</string> <string name="on" msgid="280241003226755921">"በርቷል"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ምንም"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(የሥርዓት ነባሪ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"መተግበሪያዎች የሉም"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ሌሎች NFC አገልግሎቶች"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ተመርጧል"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ተመርጧል - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ልዩ የመተግበሪያ መዳረሻ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ትላንት በ<xliff:g id="TIME_DATE">%1$s</xliff:g> ላይ ተደርስበታል"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"በ<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ላይ ተደርስበታል"</string> <string name="test_otp_msg" msgid="7559110574222727550">"የአንድ ጊዜ የይለፍ ቃልዎ 132435 ነው"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"የተገደቡ ቅንብሮችን ፍቀድ"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"የተገደበ ቅንብር"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ለእርስዎ ደህንነት ሲባል ይህ ቅንብር በአሁኑ ጊዜ አይገኝም።"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"በስልክ ጥሪ ላይ ሳለ እርምጃ አይገኝም"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"መተግበሪያዎች በስልክ ጥሪ ወቅት ሌሎች መተግበሪያዎችን እንዲጭኑ መፍቀድ አይፈቀድም።\n\n አጭበርባሪዎች ብዙ ጊዜ እንደዚህ ዓይነት እርምጃ በስልክ ጥሪ ውይይቶች ወቅት ይጠይቃሉ፣ ስለዚህ እርስዎን ለመጠበቅ ታግዷል። ይህን እርምጃ በማያውቁት ሰው እንዲወስዱ እየተመሩ ከሆነ፣ ማጭበርበር ሊሆን ይችላል።"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"መተግበሪያ የ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> መዳረሻ ተከልክሏል"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"መተግበሪያው የእርስዎን የግል እና የፋይናንስ መረጃ አደጋ ላይ የሚጥል አደገኛ ፈቃድ እንዲደርስ ጠይቋል።<xliff:g id="ID_1"><br><br></xliff:g>ያለዚህ የተገደበ ፈቃድ መተግበሪያው በትክክል ላይሰራ ይችላል። <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>እንዴት መዳረሻን እንደሚፈቅዱ ይረዱ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"መተግበሪያ ነባሪ <xliff:g id="ROLE_NAME">%1$s</xliff:g> የመሆን መዳረሻ ተከልክሏል"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"እሺ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"የፈቃድ ጥያቄ ታፍኗል"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ይህ መተግበሪያ ተጨማሪ ፈቃዶችን እየጠየቀ ነው፣ ነገር ግን ፈቃዶች በዥረት ክፍለ ጊዜ ውስጥ ሊሰጡ አይችሉም። መጀመሪያ በስልክዎ ላይ ፈቃድ ይስጡ።"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ለአደጋ ጥሪ ወይም ጽሁፍ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"አካባቢ ወደ ድንገተኛ አደጋ አገልግሎቶች ተልኳል"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ይህ መተግበሪያ ወደ የአደጋ ጊዜ ቁጥር ጥሪ በሚደረግበት ወይም ጽሁፍ በሚላክበት ጊዜ የመሣሪያዎን አካባቢ ደርሷል መተግበሪያው የአካባቢ ፈቃድ ሳይኖረው ወይም የመሣሪያው አካባቢ ሲጠፋም እንኳን ይህ ሊከሰት ይችላል። "<a href="https://support.google.com/android/answer/9319337">"የበለጠ ለመረዳት"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ስልክዎን ይፈትሹ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ወደ ስልክዎ መድረስ አልተቻለም"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"እንደገና ይሞክር?"</string> </resources> diff --git a/PermissionController/res/values-ar-v35/strings.xml b/PermissionController/res/values-ar-v35/strings.xml new file mode 100644 index 000000000..6042920c0 --- /dev/null +++ b/PermissionController/res/values-ar-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"الأمان"</string> +</resources> diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml index 894b619d5..0b463c09d 100644 --- a/PermissionController/res/values-ar/strings.xml +++ b/PermissionController/res/values-ar/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"أذونات"</string> <string name="cancel" msgid="8943320028373963831">"إلغاء"</string> <string name="back" msgid="6249950659061523680">"رجوع"</string> + <string name="dialog_close" msgid="6840699812532384661">"إغلاق"</string> <string name="available" msgid="6007778121920339498">"متاح"</string> <string name="blocked" msgid="9195547604866033708">"محظور"</string> <string name="on" msgid="280241003226755921">"مفعَّلة"</string> @@ -195,7 +196,7 @@ <string name="precise_image_description" msgid="6349638632303619872">"الموقع الجغرافي الدقيق"</string> <string name="approximate_image_description" msgid="938803699637069884">"الموقع الجغرافي التقريبي"</string> <string name="app_permission_location_accuracy" msgid="7166912915040018669">"استخدام الموقع الجغرافي الدقيق"</string> - <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"عندما يكون الموقع الجغرافي الدقيق غير مفعّل، يمكن للتطبيقات الوصول إلى الموقع الجغرافي التقريبي"</string> + <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"التطبيقات يمكنها الوصول إلى الموقع الجغرافي التقريبي عندما يكون الموقع الجغرافي الدقيق غير مفعّل"</string> <string name="app_permission_title" msgid="2090897901051370711">"إذن <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header" msgid="2951363137032603806">"منح هذا التطبيق الإذن بالوصول إلى <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"إذن \"<xliff:g id="PERM">%1$s</xliff:g>\" لهذا التطبيق على \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"غير محدَّد"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(الإعداد التلقائي للنظام)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ليست هناك تطبيقات."</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"خدمات NFC الأخرى"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"التطبيق المُختار"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"التطبيق المُختار - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"أذونات خاصة للتطبيقات"</string> @@ -578,7 +580,7 @@ <string name="security_settings" msgid="3808106921175271317">"إعدادات الأمان"</string> <string name="sensor_permissions_qs" msgid="1022267900031317472">"الأذونات"</string> <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"الأمان والخصوصية"</string> - <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"فحص الحالة"</string> + <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"التحقّق من الحالة"</string> <string name="privacy_controls_qs" msgid="5780144882040591169">"عناصر التحكّم في خصوصيتك"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"إعدادات إضافية"</string> <string name="camera_toggle_label_qs" msgid="3880261453066157285">"الوصول إلى الكاميرا"</string> @@ -669,11 +671,14 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"آخر استخدام للإذن أمس: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"آخر استخدام للإذن في <xliff:g id="TIME_DATE_0">%1$s</xliff:g>: <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"كلمة المرور الصالحة لمرة واحدة هي 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"السماح بالإعدادات المحظورة"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"الإعداد محظور"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"للحفاظ على أمانك، هذا الإعداد غير متوفِّر حاليًا."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"لا يتوفّر هذا الإجراء أثناء إجراء مكالمة هاتفية"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"لا يُسمح للتطبيقات بتثبيت تطبيقات أخرى أثناء إجراء مكالمة هاتفية.\n\n يطلب المخادِعون غالبًا هذا النوع من الإجراءات أثناء المكالمات الهاتفية، لذا تم حظره لحماية بياناتك. إذا طلبَ منك شخص غير معروف اتّخاذ هذا الإجراء، قد يكون ذلك عملية خداع."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"تم منع التطبيق من الوصول إلى <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"طلب التطبيق أحد أذونات الوصول إلى المعلومات الحساسة، ما قد يعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1"><br><br></xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذا الإذن المقيَّد. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>مزيد من المعلومات حول كيفية منح الأذونات</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"لم يتم السماح للتطبيق بأن يُستخدَم كـ <xliff:g id="ROLE_NAME">%1$s</xliff:g> تلقائيًا"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"لم يُسمَح للتطبيق بأن يكون <xliff:g id="ROLE_NAME">%1$s</xliff:g> التلقائي"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"طلب التطبيق أذونات الوصول إلى المعلومات الحساسة، ما قد يعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1"><br><br></xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذه الأذونات المقيَّدة. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>مزيد من المعلومات حول كيفية منح الأذونات</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"تم منع التطبيق من الوصول"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"إذا منحت التطبيق هذا الإذن، يمكن أن تتعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1"><br><br></xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذا الإذن المقيَّد. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>مزيد من المعلومات حول كيفية منح الأذونات</a>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"حسنًا"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"تم إلغاء طلب الحصول على الإذن"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"يطلب هذا التطبيق الحصول على أذونات إضافية، ولكن لا يمكن منح أذونات في جلسة بث. امنح الإذن على هاتفك أولاً."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"مراسلة نصية أو مكالمة طوارئ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"تم إرسال بيانات الموقع الجغرافي إلى خدمات طوارئ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"وصل هذا التطبيق إلى الموقع الجغرافي لجهازك أثناء إجراء اتصال برقم طوارئ أو مراسلته نصيًا. يمكن أن يحدث ذلك حتى إذا لم يحصل التطبيق على إذن تحديد الموقع الجغرافي أو لم يتم تفعيل إعدادات الموقع الجغرافي للجهاز. "<a href="https://support.google.com/android/answer/9319337">"مزيد من المعلومات"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"يرجى التحقّق من هاتفك"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"تعذَّر الوصول إلى هاتفك"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"هل تريد إعادة المحاولة؟"</string> </resources> diff --git a/PermissionController/res/values-as-v35/strings.xml b/PermissionController/res/values-as-v35/strings.xml new file mode 100644 index 000000000..37ac47117 --- /dev/null +++ b/PermissionController/res/values-as-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"সুৰক্ষা"</string> +</resources> diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml index e46e8bafe..2e6c890b0 100644 --- a/PermissionController/res/values-as/strings.xml +++ b/PermissionController/res/values-as/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"অনুমতি"</string> <string name="cancel" msgid="8943320028373963831">"বাতিল কৰক"</string> <string name="back" msgid="6249950659061523680">"উভতি যাওক"</string> + <string name="dialog_close" msgid="6840699812532384661">"বন্ধ কৰক"</string> <string name="available" msgid="6007778121920339498">"উপলব্ধ"</string> <string name="blocked" msgid="9195547604866033708">"অৱৰোধ কৰা আছে"</string> <string name="on" msgid="280241003226755921">"অন আছে"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"নাই"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ছিষ্টেম ডিফ\'ল্ট)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"কোনো এপ্ নাই"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"অন্য NFC সেৱা"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"বাছনিকৃত"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"বাছনিকৃত - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"বিশেষ এপ্ এক্সেছ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"কালি <xliff:g id="TIME_DATE">%1$s</xliff:g>ত এক্সেছ কৰিছিল"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>ত এক্সেছ কৰিছিল"</string> <string name="test_otp_msg" msgid="7559110574222727550">"আপোনাৰ এবাৰ ব্যৱহাৰযোগ্য পাছৱর্ড হৈছে ১৩২৪৩৫"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"প্ৰতিবন্ধিত ছেটিঙৰ অনুমতি দিয়ক"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"প্ৰতিবন্ধিত ছেটিং"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপোনাৰ সুৰক্ষাৰ বাবে, এই ছেটিংটো বৰ্তমান উপলব্ধ নহয়।"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ফ’ন কল চলি থকাৰ সময়ত এই কাৰ্যটো কৰিব নোৱাৰি"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ফ’ন কল চলি থকাৰ সময়ত এপক অন্য এপ্ ইনষ্টল কৰিবলৈ দিয়াৰ অনুমতি নাই।\n\n ফ’ন কল চলি থকাৰ সময়ত প্ৰৱঞ্চকে প্ৰায়েই এই ধৰণৰ কাৰ্য কৰিবলৈ অনুৰোধ কৰে, গতিকে আপোনাক সুৰক্ষিত কৰিবলৈ এয়া অৱৰোধ কৰা হয়। যদি আপোনাক আপুনি চিনি নোপোৱা কোনো লোকে এই কাৰ্যটো কৰিবলৈ নিৰ্দেশনা দিয়ে, সেয়া প্ৰৱঞ্চনা হ’ব পাৰে।"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"এপ্টোক <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"এপ্টোৱে সংবেদনশীল অনুমতি এক্সেছ কৰিবলৈ অনুৰোধ কৰিছে, যিটোৱে আপোনাৰ ব্যক্তিগত আৰু বিত্তীয় তথ্য বিপদাপন্ন কৰিব পাৰে।<xliff:g id="ID_1"><br><br></xliff:g>এপ্টোৱে এই প্ৰতিবন্ধিত অনুমতিটোৰ অবিহনে সঠিককৈ কাম নকৰাটো সম্ভৱ হ’ব পাৰে। <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>কেনেকৈ এক্সেছৰ অনুমতি দিব লাগে জানক</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"এপ্টোক ডিফ’ল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ঠিক আছে"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"অনুমতিৰ অনুৰোধ অৱদমন কৰা হৈছে"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"এই এপ্টোৱে অতিৰিক্ত অনুমতিৰ বাবে অনুৰোধ কৰিছে, কিন্তু ষ্ট্ৰীমিং ছেশ্বনত অনুমতি দিব নোৱাৰি। আপোনাৰ ফ’নটোত প্ৰথমে অনুমতি দিয়ক।"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"জৰুৰীকালীন কল বা পাঠ বাৰ্তাৰ বাবে"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"অৱস্থান জৰুৰীকালীন সেৱালৈ পঠিওৱা হৈছে"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"এটা জৰুৰীকালীন নম্বৰলৈ কল কৰোঁতে বা বাৰ্তা পঠিয়াওঁতে এই এপ্টোৱে আপোনাৰ ডিভাইচৰ অৱস্থান এক্সেছ কৰিছে। এপ্টোৰ অৱস্থানৰ অনুমতি নাথাকিলে বা ডিভাইচৰ অৱস্থান অফ হৈ থাকিলেও এনে হ’ব পাৰে। "<a href="https://support.google.com/android/answer/9319337">"অধিক জানক"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"আপোনাৰ ফ’নটো চাওক"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"আপোনাৰ ফ’নটোৰ সৈতে সংযোগ কৰিব পৰা নগ’ল"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"পুনৰ চেষ্টা কৰিবনে?"</string> </resources> diff --git a/PermissionController/res/values-az-v35/strings.xml b/PermissionController/res/values-az-v35/strings.xml new file mode 100644 index 000000000..7ffd58cc1 --- /dev/null +++ b/PermissionController/res/values-az-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Güvənlik"</string> +</resources> diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml index 773991122..4a7ddc91f 100644 --- a/PermissionController/res/values-az/strings.xml +++ b/PermissionController/res/values-az/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"icazələr"</string> <string name="cancel" msgid="8943320028373963831">"Ləğv edin"</string> <string name="back" msgid="6249950659061523680">"Geri"</string> + <string name="dialog_close" msgid="6840699812532384661">"Bağlayın"</string> <string name="available" msgid="6007778121920339498">"Əlçatan"</string> <string name="blocked" msgid="9195547604866033708">"Bloklanıb"</string> <string name="on" msgid="280241003226755921">"Aktiv"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Yoxdur"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sistem defoltu)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Tətbiq yoxdur"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Digər NFC xidmətləri"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Seçilib"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Seçilib - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"xüsusi tətbiq girişi"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Daxil olunub: dünən <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Daxil olunub: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Birdəfəlik parol: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Məhdudlaşdırılmış ayarlara icazə verin"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Məhdudlaşdırılmış ayar"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvənlik üçün bu ayar əlçatan deyil."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Telefon zəngi zamanı əməliyyat mümkün deyil"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon zəngi zamanı tətbiqlərin digər tətbiqləri quraşdırmağına icazə verilmir.\n\n Fırıldaqçılar, adətən, telefon danışıqları zamanı bu cür əməliyyatları həyata keçirməyinizi tələb edirlər, ona görə də sizi qorumaq bloklanıb. Tanımadığınız şəxs tərəfindən bu əməliyyatları həyata keçirməyiniz istənilsə, bu, fırıldaqçılıq ola bilər."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tətbiqə <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> icazəsinə giriş verilmədi"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Tətbiq şəxsi və maliyyə məlumatlarınızı riskə ata biləcək həssas məlumat icazəsinə giriş istədi.<xliff:g id="ID_1"><br><br></xliff:g>Bu məhdud icazə olmadan tətbiq düzgün işləməyə bilər. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Girişə icazə vermək haqqında ətraflı</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tətbiqə defolt <xliff:g id="ROLE_NAME">%1$s</xliff:g> roluna giriş verilmədi"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"İcazə sorğusu dayandırıldı"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu tətbiqə əlavə icazələr lazımdır, lakin yayım sessiyasında icazə vermək olmur. Əvvəlcə telefonda icazə verin."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Təcili zəng və ya mətn mesajı üçün"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Məkan təcili xidmətlərə göndərildi"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Bu tətbiq təcili nömrəyə zəng edərkən və ya mesaj göndərərkən cihazınızın məkanına daxil oldu. Bu hal hətta tətbiqin məkan icazəsi olmadıqda və ya cihaz məkanı sönülü olduqda da baş verə bilər. "<a href="https://support.google.com/android/answer/9319337">"Ətraflı məlumat"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Telefonu yoxlayın"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Telefona qoşulmaq olmur"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Təkrar cəhd edilsin?"</string> </resources> diff --git a/PermissionController/res/values-b+sr+Latn-v35/strings.xml b/PermissionController/res/values-b+sr+Latn-v35/strings.xml new file mode 100644 index 000000000..9ad2d24c7 --- /dev/null +++ b/PermissionController/res/values-b+sr+Latn-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Bezbednost"</string> +</resources> diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml index 62f21a63f..e26a7599a 100644 --- a/PermissionController/res/values-b+sr+Latn/strings.xml +++ b/PermissionController/res/values-b+sr+Latn/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"dozvole"</string> <string name="cancel" msgid="8943320028373963831">"Otkaži"</string> <string name="back" msgid="6249950659061523680">"Nazad"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string> <string name="available" msgid="6007778121920339498">"Dostupno"</string> <string name="blocked" msgid="9195547604866033708">"Blokirano"</string> <string name="on" msgid="280241003226755921">"Uključeno"</string> @@ -368,7 +369,7 @@ <string name="role_dialer_search_keywords" msgid="3324448983559188087">"brojčanik"</string> <string name="role_sms_label" msgid="8456999857547686640">"Podrazumevana apl. za SMS"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"Aplikacija za SMS"</string> - <string name="role_sms_description" msgid="3424020199148153513">"Aplikacije koje vam omogućavaju da koristite broj telefona da biste slali i primali kratke SMS-ove, slike, video snimke i još toga"</string> + <string name="role_sms_description" msgid="3424020199148153513">"Aplikacije koje vam omogućavaju da koristite broj telefona da biste slali i primali kratke tekstualne poruke, slike, video snimke i drugo"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikacju za SMS?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Ova aplikacija će dobiti pristup kameri, kontaktima, fajlovima i medijima, mikrofonu, telefonu i SMS-ovima"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, slanje SMS-ova, poruke, slanje poruka"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ništa"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Podrazumevana sistemska)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Druge NFC usluge"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Izabrano"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Izabrano – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"poseban pristup aplikacija"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> želi da otpremi informacije za otklanjanje grešaka."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Želite da delite podatke o otklanjanju grešaka?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistem je otkrio problem."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> traži da otpremi izveštaj o greškama sa ovog uređaja koji je napravljen <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izveštaji o greškama obuhvataju lične podatke o uređaju ili podatke koje su evidentirale aplikacije, na primer, korisnička imena, podatke o lokaciji, identifikatore uređaja i informacije o mreži. Delite izveštaje o greškama samo sa ljudima i aplikacijama kojima možete da poverite te informacije. Želite li da dozvolite da <xliff:g id="APP_NAME_1">%4$s</xliff:g> otpremi izveštaj o grešci?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> traži da otpremi izveštaj o greškama sa ovog uređaja koji je napravljen <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izveštaji o greškama obuhvataju lične podatke o uređaju ili podatke koje su evidentirale aplikacije, na primer, korisnička imena, podatke o lokaciji, identifikatore uređaja i informacije o mreži. Delite izveštaje o greškama samo sa ljudima i aplikacijama koje smatrate pouzdanim za te informacije. Želite li da dozvolite da <xliff:g id="APP_NAME_1">%4$s</xliff:g> otpremi izveštaj o grešci?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Došlo je do greške pri obradi izveštaja o grešci za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>. Zato je odbijeno deljenje detaljnih podataka o otklanjanju grešaka. Izvinjavamo se zbog prekida."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Dozvoli"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Odbij"</string> @@ -469,10 +471,10 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa slikama i medijima na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa kontaktima?"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa kontaktima na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"Želite da dozvolite da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji uređaja <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacija će imati pristup lokaciji samo dok koristite aplikaciju"</string> - <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string> + <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Želite da dozvolite da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string> <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji uređaja <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ova aplikacija možda želi da pristupa lokaciji sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string> <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Želite li da promenite pristup lokaciji za aplikaciju <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> @@ -514,13 +516,13 @@ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa podacima o fizičkim aktivnostima na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_camera" msgid="5123097035410002594">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima slike i video?"</string> <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima slike i video na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći da snima slike i video snimke samo dok koristite aplikaciju"</string> - <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima slike i video snimke?"</string> + <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći da snima slike i video samo dok koristite aplikaciju"</string> + <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima slike i video?"</string> <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> snima slike i video na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ova aplikacija možda želi da snima slike i video snimke sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string> + <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ova aplikacija možda želi da snima slike i video sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string> <string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Želite da promenite pristup kameri za aplikaciju <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Menjate pristup kameri za aplikaciju <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ova aplikacija želi da snima slike i video snimke sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string> + <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ova aplikacija želi da snima slike i video sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string> <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa evidencijama poziva na telefonu?"</string> <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Dozvoljavate da aplikacija <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa evidenciji telefonskih poziva na: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_phone" msgid="1829234136997316752">"Želite da dozvolite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> upućuje pozive i upravlja njima?"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupano juče: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupano: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jednokratna lozinka je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Dozvoli ograničena podešavanja"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničeno podešavanje"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ovo podešavanje je trenutno nedostupno radi vaše bezbednosti."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tokom telefonskog poziva"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nije dozvoljeno da aplikacije instaliraju druge aplikacije tokom telefonskog poziva.\n\n Prevaranti često traže ovaj tip radnje tokom telefonskog poziva, pa smo je blokirali da bismo vas zaštitili. Ako vas neko koga ne poznajete upućuje na ovu radnju, to bi mogla da bude prevara."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji nije dozvoljen pristup dozvoli: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osetljivoj dozvoli, što može da ugrozi bezbednost ličnih i finansijskih podataka.<xliff:g id="ID_1"><br><br></xliff:g>Aplikacija možda neće raditi ispravno bez ove ograničene dozvole. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako da dozvolite pristup</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji nije dozvoljen pristup da postane podrazumevana: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Važi"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahtev za dozvolu je blokiran"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ova aplikacija zahteva dodatne dozvole, ali dozvole ne mogu da se daju u sesiji strimovanja. Prvo dajte dozvolu na telefonu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za hitni poziv ili poruku"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija je poslata hitnim službama"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova aplikacija je pristupila lokaciji uređaja tokom pozivanja broja za hitne slučajeve ili slanja poruke tom broju. To može da se dogodi čak i kada aplikacija nema dozvolu za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Proverite telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Povezivanje sa telefonom nije uspelo"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Želite li da probate ponovo?"</string> </resources> diff --git a/PermissionController/res/values-be-v35/strings.xml b/PermissionController/res/values-be-v35/strings.xml new file mode 100644 index 000000000..39f076e77 --- /dev/null +++ b/PermissionController/res/values-be-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Бяспека"</string> +</resources> diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml index d0215198b..e02518d3d 100644 --- a/PermissionController/res/values-be/strings.xml +++ b/PermissionController/res/values-be/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"дазволы"</string> <string name="cancel" msgid="8943320028373963831">"Скасаваць"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Закрыць"</string> <string name="available" msgid="6007778121920339498">"Даступна"</string> <string name="blocked" msgid="9195547604866033708">"Заблакіравана"</string> <string name="on" msgid="280241003226755921">"Уключана"</string> @@ -92,7 +93,7 @@ <string name="manage_permission" msgid="2895385393037061964">"Кіраваць дазволам"</string> <string name="no_apps" msgid="2412612731628386816">"Няма праграм"</string> <string name="location_settings" msgid="3624412509133422562">"Налады месцазнаходжання"</string> - <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> з\'яўляецца службай геалакацыі для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах."</string> + <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> з’яўляецца службай геалакацыі для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах."</string> <string name="system_warning" msgid="1173400963234358816">"Калі вы адхіліце гэты дазвол, асноўныя функцыі прылады могуць перастаць працаваць належным чынам."</string> <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Гэта праграма створана для ранейшай версіі Android. Забараніўшы праграме доступ да фота і відэа, вы таксама закрыеце доступ да музыкі і іншых аўдыяфайлаў."</string> <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Гэта праграма створана для ранейшай версіі Android. Забараніўшы праграме доступ да музыкі і іншых аўдыяфайлаў, вы таксама закрыеце доступ да фота і відэа."</string> @@ -374,7 +375,7 @@ <string name="role_sms_search_keywords" msgid="8022048144395047352">"тэкставае паведамленне, SMS, паведамленні, абмен паведамленнямі"</string> <string name="role_emergency_label" msgid="7028825857206842366">"Праграма экстраннай дапамогі"</string> <string name="role_emergency_short_label" msgid="2388431453335350348">"Праграма экстраннай дапамогі"</string> - <string name="role_emergency_description" msgid="5051840234887686630">"Праграмы, якія даюць вам магчымасць: запісаць медыцынскую інфармацыю і зрабіць яе даступнай для экстраннай службы; атрымліваць абвесткі пра стыхійныя бедствы і складаныя ўмовы надвор\'я; апавяшчаць іншых пра тое, што вам патрабуецца дапамога"</string> + <string name="role_emergency_description" msgid="5051840234887686630">"Праграмы, якія даюць вам магчымасць: запісаць медыцынскую інфармацыю і зрабіць яе даступнай для экстраннай службы; атрымліваць абвесткі пра стыхійныя бедствы і складаныя ўмовы надвор’я; апавяшчаць іншых пра тое, што вам патрабуецца дапамога"</string> <string name="role_emergency_request_title" msgid="8469579020654348567">"Прызначыць \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартнай праграмай экстраннай дапамогі?"</string> <string name="role_emergency_request_description" msgid="131645948770262850">"Дазволы не патрэбныя"</string> <string name="role_emergency_search_keywords" msgid="1920007722599213358">"у выпадку надзвычайнай сітуацыі"</string> @@ -408,7 +409,7 @@ <string name="role_notes_search_keywords" msgid="7710756695666744631">"нататкі"</string> <string name="role_wallet_label" msgid="3719419175656204207">"Стандартная праграма-кашалёк"</string> <string name="role_wallet_short_label" msgid="6521288403762457452">"Праграма-кашалёк"</string> - <string name="role_wallet_description" msgid="3726535836165949838">"У праграмах-кашальках можна захоўваць крэдытныя карткі, карткі лаяльнасці, ключы ад аўтамабіля і іншыя лічбавыя аб\'екты, што дае магчымасць ажыццяўляць розныя формы трансакцый."</string> + <string name="role_wallet_description" msgid="3726535836165949838">"У праграмах-кашальках можна захоўваць крэдытныя карткі, карткі лаяльнасці, ключы ад аўтамабіля і іншыя лічбавыя аб’екты, што дае магчымасць ажыццяўляць розныя формы трансакцый."</string> <string name="role_wallet_request_title" msgid="4770217108262737093">"Задаць у якасці стандартнай праграмы-кашалька праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="role_wallet_request_description" msgid="6305487425777483053">"Дазволы не патрэбныя"</string> <string name="request_role_current_default" msgid="738722892438247184">"Цяперашняя стандартная"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Няма"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Стандартная сістэмная)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Няма праграм"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Іншыя сэрвісы NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Выбрана"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Выбрана – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"спецыяльны доступ для праграм"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Апошні раз доступ быў атрыманы ўчора ў <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Апошні раз доступ быў атрыманы ў <xliff:g id="TIME_DATE_1">%2$s</xliff:g> <xliff:g id="TIME_DATE_0">%1$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ваш аднаразовы пароль: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Дазволіць абмежаваныя налады"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налада з абмежаваным доступам"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"У мэтах бяспекі гэта налада цяпер недаступная."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дзеянне недаступнае падчас тэлефоннага выкліку"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Падчас тэлефоннага выкліку забараняецца даваць дазвол праграмам усталёўваць іншыя праграмы.\n\n У тэлефонных размовах махляры часта просяць усталяваць праграмы, таму ў мэтах бяспекі мы блакіруем такія дзеянні. Запыт ад невядомай вам асобы выканаць падобнае дзеянне можа расцэньвацца як махлярства."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Праграме адмоўлена ў дазволе \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Праграма запытала дазвол на доступ да канфідэнцыяльнай інфармацыі. Калі вы яго дасце, ваша асабістая і фінансавая інфармацыя можа аказацца ў небяспецы.<xliff:g id="ID_1"><br><br></xliff:g>Магчыма, без гэтага абмежаванага дазволу праграма не будзе працаваць належным чынам. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Даведацца, як дазволіць доступ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Праграме адмоўлена ў дазволе стандартна выконваць наступную ролю: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запыт дазволаў заблакіраваны"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Гэта праграма запытвае дадатковыя дазволы, якія немагчыма даць падчас трансляцыі. Спачатку дайце дазвол на тэлефоне."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Для выкліку або SMS экстранным службам"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Геаданыя адпраўляюцца экстранным службам"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Калі вы тэлефанавалі або адпраўлялі SMS на нумар экстраннай службы, гэта праграма атрымлівала доступ да геаданых прылады. Гэта магчыма, нават калі ў праграмы няма дазволу на доступ да геаданых і калі вызначэнне геаданых прылады выключана. "<a href="https://support.google.com/android/answer/9319337">"Даведацца больш"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Працягніце на тэлефоне"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Не ўдалося падключыцца да тэлефона"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Паўтарыць спробу?"</string> </resources> diff --git a/PermissionController/res/values-bg-v35/strings.xml b/PermissionController/res/values-bg-v35/strings.xml new file mode 100644 index 000000000..72f4a8562 --- /dev/null +++ b/PermissionController/res/values-bg-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Сигурност"</string> +</resources> diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml index ffbfd5a1c..f60db861f 100644 --- a/PermissionController/res/values-bg/strings.xml +++ b/PermissionController/res/values-bg/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"разрешения"</string> <string name="cancel" msgid="8943320028373963831">"Отказ"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Затваряне"</string> <string name="available" msgid="6007778121920339498">"Налице"</string> <string name="blocked" msgid="9195547604866033708">"Блокирано"</string> <string name="on" msgid="280241003226755921">"Вкл."</string> @@ -59,7 +60,7 @@ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Разрешаване на управлението на всички файлове"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Разрешаване на достъп до мултимедийните файлове"</string> <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Приложения"</string> - <string name="app_permissions" msgid="3369917736607944781">"Разрешения за приложенията"</string> + <string name="app_permissions" msgid="3369917736607944781">"Разрешения за приложението"</string> <string name="unused_apps" msgid="2058057455175955094">"Неизползвани приложения"</string> <string name="edit_photos_description" msgid="5540108003480078892">"Редактиране на избраните снимки за това приложение"</string> <string name="no_unused_apps" msgid="12809387670415295">"Няма неизползвани приложения"</string> @@ -194,7 +195,7 @@ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Разрешаване на ограничения достъп"</string> <string name="precise_image_description" msgid="6349638632303619872">"Точно местоположение"</string> <string name="approximate_image_description" msgid="938803699637069884">"Приблизително местоположение"</string> - <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Използване на точното местоположение"</string> + <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Използване на точното местоположение"</string> <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Когато точното местоположение е изключено, приложенията могат да осъществяват достъп до приблизителното ви местоположение"</string> <string name="app_permission_title" msgid="2090897901051370711">"Разрешение за: <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header" msgid="2951363137032603806">"Достъп до „<xliff:g id="PERM">%1$s</xliff:g>“ за това приложение"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Няма"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Стандартно за системата)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Няма приложения"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Други услуги за NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Избрано"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Избрано – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"специален достъп за приложението"</string> @@ -469,7 +471,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до снимките и мултимедията на <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до контактите ви?"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до контактите ви на <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до местоположението на това устройство?"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да получи достъп до местоположението на това устройство?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до местоположението на <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Само когато използвате приложението, то ще има достъп до местоположението"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до местоположението на това устройство?"</string> @@ -669,11 +671,14 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Осъществен достъп: вчера, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Осъществен достъп: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Еднократната ви парола е 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Разрешаване на ограничените настройки"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена настройка"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"От съображения за сигурност понастоящем тази настройка не е налице."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Действието не е налице по време на тел. обаждане"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"По време на телефонно обаждане не можете да разрешавате на приложенията да инсталират други приложения.\n\nТова действие е блокирано от съображения за сигурност, защото често се използва от измамници по време на телефонни разговори. Ако човек, когото не познавате, ви напътства, за да предприемете това действие, възможно е да става дума за измама."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"На приложението бе отказан достъп до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложението поиска разрешение за достъп до поверителни данни, което може да изложи на риск личната или финансовата ви информация.<xliff:g id="ID_1"><br><br></xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Научете как да разрешите достъпа</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"На приложението бе забранено да изпълнява функциите на основно <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Приложението не получи разрешение да бъде по подразбиране: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложението поиска разрешения за достъп до поверителни данни, които може да изложат на риск личната или финансовата ви информация.<xliff:g id="ID_1"><br><br></xliff:g>Възможно е приложението да не работи правилно без тези ограничени разрешения. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Научете как да разрешите достъпа</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"На приложението бе отказан достъп"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Достъпът до това разрешение може да изложи на риск личната и финансовата ви информация.<xliff:g id="ID_1"><br><br></xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Научете как да разрешите достъпа</a>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Заявката за разрешения е блокирана"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Това приложение изисква допълнителни разрешения, но те не могат да бъдат предоставени в сесия за поточно предаване. Първо ги предоставете на телефона си."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"За спешно обаждане или изпращане на съобщение"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Местоположението е изпратено до службите за спешни случаи"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Това приложение осъществи достъп до местоположението на устройството ви по време на обаждане или изпращане на текстово съобщение до номер за спешни случаи. Това може да се случи дори когато приложението няма разрешение за достъп до местоположението или местоположението на устройството е изключено. "<a href="https://support.google.com/android/answer/9319337">"Научете повече"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Проверете телефона си"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Не бе установена връзка с телефона ви"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Да се опита ли отново?"</string> </resources> diff --git a/PermissionController/res/values-bn-v35/strings.xml b/PermissionController/res/values-bn-v35/strings.xml new file mode 100644 index 000000000..066c4ad5a --- /dev/null +++ b/PermissionController/res/values-bn-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"সুরক্ষা"</string> +</resources> diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml index cb20add5e..27c5f85cc 100644 --- a/PermissionController/res/values-bn/strings.xml +++ b/PermissionController/res/values-bn/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"অনুমতি"</string> <string name="cancel" msgid="8943320028373963831">"বাতিল করুন"</string> <string name="back" msgid="6249950659061523680">"ফিরুন"</string> + <string name="dialog_close" msgid="6840699812532384661">"বন্ধ করুন"</string> <string name="available" msgid="6007778121920339498">"উপলভ্য"</string> <string name="blocked" msgid="9195547604866033708">"ব্লক করা হয়েছে"</string> <string name="on" msgid="280241003226755921">"চালু করা আছে"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"কোনওটিই নয়"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(সিস্টেম ডিফল্ট)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"কোনও অ্যাপ নেই"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"অন্যান্য NFC পরিষেবা"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"বেছে নেওয়া হয়েছে"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"বেছে নেওয়া হয়েছে - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"অ্যাপের বিশেষ অ্যাক্সেস"</string> @@ -528,7 +530,7 @@ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে সেন্সর থেকে আপনার ভাইটাল সাইনের ডেটা অ্যাক্সেস করতে দেবেন?"</string> <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপকে <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> ডিভাইসে আপনার ভাইটাল সাইন সম্পর্কিত সেন্সর ডেটা অ্যাক্সেসের অনুমতি দেবেন?"</string> <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"এই অ্যাপ সবসময় শারীরবৃত্তীয় লক্ষণ সংক্রান্ত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি চায়, এমনকী আপনি যখন অ্যাপটি ব্যবহার করছেন না, তখনও। এই পরিবর্তন চালু করতে, "<annotation id="link">"সেটিংসে যান।"</annotation></string> - <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে আপনার বেঁচে থাকার গুরুত্বপূর্ণ লক্ষণ সম্পর্কিত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি দিতে চান?"</string> + <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে আপনার ভাইটাল সাইন সম্পর্কিত সেন্সর ডেটা অ্যাক্সেস করার অনুমতি দিতে চান?"</string> <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপকে <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> ডিভাইসে আপনার ভাইটাল সাইন সম্পর্কিত সেন্সর ডেটা অ্যাক্সেসের অনুমতি দেবেন?"</string> <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"অ্যাপটি ব্যবহার না করলেও, সেটিকে সবসময় বডি সেন্সর ডেটাতে অ্যাক্সেস দিতে "<annotation id="link">"সেটিংসে যান"</annotation>"।"</string> <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"ব্যবহার করার সময় <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপকে বডি সেন্সর ডেটার অ্যাক্সেস দিয়ে রাখতে চান?"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"গতকাল <xliff:g id="TIME_DATE">%1$s</xliff:g>-এ অ্যাক্সেস করা হয়েছে"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>-এ অ্যাক্সেস করা হয়েছে"</string> <string name="test_otp_msg" msgid="7559110574222727550">"আপনার একবার ব্যবহারের পাসওয়ার্ড হল ১২৩৪৩৫"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"বিধিনিষেধযুক্ত সেটিংসের অনুমতি দিন"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"বিধিনিষেধযুক্ত সেটিংস"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপনার নিরাপত্তার জন্য, এই সেটিং বর্তমানে উপলভ্য নেই।"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ফোন কলের সময় অ্যাকশন নেওয়া যাবে না"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ফোন কলের সময় অ্যাপকে অন্য অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।\n\n ফোন কলের সময় স্ক্যামার প্রায়ই এই ধরনের অ্যাকশনের অনুরোধ করেন, তাই আপনাকে সুরক্ষিত রাখতে এটি ব্লক করা হয়েছে। আপনি চেনেন না এমন কেউ আপনাকে এই অ্যাকশন নিতে বললে, সেটি স্ক্যাম হতে পারে।"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"অ্যাপকে <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> অ্যাক্সেস করতে দেওয়া হয়নি"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"অ্যাপটি সংবেদনশীল অনুমতি অ্যাক্সেস করার অনুরোধ জানিয়েছে, যার জন্য আপনার ব্যক্তিগত ও আর্থিক অবস্থা সম্পর্কিত তথ্যের ক্ষেত্রে ঝুঁকির সম্ভাবনা থাকতে পারে।<xliff:g id="ID_1"><br><br></xliff:g>এই সীমাবদ্ধ অনুমতি ছাড়া অ্যাপটি সঠিকভাবে কাজ না করার সম্ভাবনাও আছে। <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>কীভাবে অ্যাক্সেস করার অনুমতি দেবেন সেই সম্পর্কে জানুন</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"অ্যাপকে ডিফল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g> হিসেবে কাজ করার অ্যাক্সেস দেওয়া হয়নি"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ঠিক আছে"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"অনুমতির অনুরোধ ব্লক করা হয়েছে"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"এই অ্যাপ অতিরিক্ত অনুমতির জন্য অনুরোধ করছে, কিন্তু স্ট্রিমিং সেশন চলাকালীন অনুমতি দেওয়া যাবে না। প্রথমে আপনার ফোনে অনুমতি দিন।"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"জরুরি কল বা মেসেজের জন্য"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"জরুরি পরিষেবায় লোকেশন পাঠানো হয়েছে"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"আপনি যখন একটি জরুরি নম্বরে কল বা মেসেজ পাঠানোর সময়, অ্যাপটি আপনার ডিভাইসের লোকেশন অ্যাক্সেস করেছে। অ্যাপের ডিভাইসের লোকেশন অ্যাক্সেস করার অনুমতি না থাকলে অথবা লোকেশন বন্ধ থাকলেও এটি হতে পারে। "<a href="https://support.google.com/android/answer/9319337">"আরও জানুন"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"আপনার ফোন চেক করুন"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"আপনার ফোনে কানেক্ট করা যায়নি"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"আবার চেষ্টা করতে চান?"</string> </resources> diff --git a/PermissionController/res/values-bs-v35/strings.xml b/PermissionController/res/values-bs-v35/strings.xml new file mode 100644 index 000000000..71b83b6c9 --- /dev/null +++ b/PermissionController/res/values-bs-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sigurnost"</string> +</resources> diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml index 56ee25058..95c1d96b6 100644 --- a/PermissionController/res/values-bs/strings.xml +++ b/PermissionController/res/values-bs/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"odobrenja"</string> <string name="cancel" msgid="8943320028373963831">"Otkaži"</string> <string name="back" msgid="6249950659061523680">"Nazad"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string> <string name="available" msgid="6007778121920339498">"Dostupno"</string> <string name="blocked" msgid="9195547604866033708">"Blokirano"</string> <string name="on" msgid="280241003226755921">"Uključeno"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nema"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sistemski zadano)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Druge NFC usluge"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Odabrano"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Odabrano – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"poseban pristup aplikacijama"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> želi otpremiti informacije o otklanjanju grešaka."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Podijeliti podatke o otklanjanju grešaka?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistem je otkrio problem."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> traži da s ovog uređaja otpremi izvještaj o greškama koji je izvršen <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izvještaji o greškama uključuju lične informacije o vašem uređaju ili akreditivima za prijave u aplikacije, naprimjer korisnička imena, podatke o lokaciji, identifikatore uređaja i informacije o mreži. Izvještaje o greškama dijelite samo s osobama i aplikacijama kojim vjerujete. Dozvoliti aplikaciji <xliff:g id="APP_NAME_1">%4$s</xliff:g> da otpremi izvještaj o greškama?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> traži da s ovog uređaja otpremi izvještaj o greškama napravljen <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izvještaji o greškama uključuju lične informacije o vašem uređaju ili koje su zabilježile aplikacije, npr. korisnička imena, podatke o lokaciji, identifikatore uređaja i informacije o mreži. Izvještaje o greškama dijelite samo s osobama i aplikacijama kojim vjerujete. Dozvoliti aplikaciji <xliff:g id="APP_NAME_1">%4$s</xliff:g> da otpremi izvještaj o greškama?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Došlo je do greške prilikom obrade izvještaja o greškama za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>. Stoga je odbijeno dijeljenje detaljnih podataka o otklanjanju grešaka. Izvinjavamo se na prekidu."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Dozvoli"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Odbij"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupljeno je jučer u <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupljeno je <xliff:g id="TIME_DATE_0">%1$s</xliff:g> u <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jednokratna lozinka je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Dozvoli ograničene postavke"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti postavka trenutno nije dostupna."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tokom telefonskog poziva"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Dozvoljavanje aplikacijama da instaliraju druge aplikacije nije dozvoljeno tokom telefonskog poziva.\n\n Prevaranti često traže ovu vrstu radnje tokom telefonskih razgovora, pa ovo blokiramo da vas zaštitimo. Ako neko koga ne poznajete traži da poduzmete ovu radnju, to može biti prevara."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup odobrenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osjetljivom odobrenju, što može ugroziti vaše lične i finansijske informacije.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće ispravno raditi bez ovog ograničenog odobrenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako dozvoliti pristup</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je odbijen pristup da bude zadana <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -680,5 +685,11 @@ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Uredu"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahtjev za odobrenje je potisnut"</string> - <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prijenosa. Najprije dajte odobrenje na telefonu."</string> + <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prenosa. Najprije dajte odobrenje na telefonu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za hitni poziv ili poruku"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija je poslana hitnim službama"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova aplikacija je pristupila lokaciji uređaja tokom poziva ili slanja poruke broju za hitne slučajeve. To se može dogoditi čak i kada aplikacija nema odobrenje za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Provjerite telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nije se moguće povezati s telefonom"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Pokušati ponovo?"</string> </resources> diff --git a/PermissionController/res/values-ca-v35/strings.xml b/PermissionController/res/values-ca-v35/strings.xml new file mode 100644 index 000000000..10bff4b9c --- /dev/null +++ b/PermissionController/res/values-ca-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Seguretat"</string> +</resources> diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml index bbb20307b..fd1e23c51 100644 --- a/PermissionController/res/values-ca/strings.xml +++ b/PermissionController/res/values-ca/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string> <string name="cancel" msgid="8943320028373963831">"Cancel·la"</string> <string name="back" msgid="6249950659061523680">"Enrere"</string> + <string name="dialog_close" msgid="6840699812532384661">"Tanca"</string> <string name="available" msgid="6007778121920339498">"Disponible"</string> <string name="blocked" msgid="9195547604866033708">"Bloquejat"</string> <string name="on" msgid="280241003226755921">"Activat"</string> @@ -345,7 +346,7 @@ <string name="no_apps_allowed" msgid="7718822655254468631">"Cap aplicació amb permís"</string> <string name="no_apps_allowed_full" msgid="8011716991498934104">"Cap aplicació té permís per accedir a tots els fitxers"</string> <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Cap aplicació té permís per accedir només a fitxers multimèdia"</string> - <string name="no_apps_denied" msgid="7663435886986784743">"Cap aplicació amb permís denegat"</string> + <string name="no_apps_denied" msgid="7663435886986784743">"Cap aplicació denegada"</string> <string name="car_permission_selected" msgid="180837028920791596">"Seleccionat"</string> <string name="settings" msgid="5409109923158713323">"Configuració"</string> <string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> té accés complet al dispositiu"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Cap"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Opció predeterminada del sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Cap aplicació"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Altres serveis NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Seleccionada"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Seleccionada: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accés especial d\'aplicacions"</string> @@ -455,7 +457,7 @@ <string name="incident_report_dialog_title" msgid="669104389325204095">"Vols compartir les dades de depuració?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"El sistema ha detectat un problema."</string> <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sol·licita penjar un informe d\'errors des d\'aquest dispositiu generat el dia <xliff:g id="DATE">%2$s</xliff:g> (<xliff:g id="TIME">%3$s</xliff:g>). Els informes d\'errors inclouen informació personal sobre el dispositiu o informació registrada per les aplicacions, com ara noms d\'usuaris, dades de la ubicació, identificadors del dispositiu i informació de la xarxa. Comparteix informes d\'errors només amb persones i aplicacions de confiança. Vols permetre que <xliff:g id="APP_NAME_1">%4$s</xliff:g> pengi un informe d\'errors?"</string> - <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"S\'ha produït un error en processar l\'informe d\'errors de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> i, per tant, s\'han denegat les dades de depuració detallades. Sentim la interrupció."</string> + <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Hi ha hagut un error en processar l\'informe d\'errors de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> i, per tant, s\'han denegat les dades de depuració detallades. Sentim la interrupció."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Permet"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Denega"</string> <string name="adjust_user_sensitive_title" msgid="4196724451314280527">"Configuració avançada"</string> @@ -469,7 +471,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a les fotos i al contingut multimèdia del dispositiu <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als contactes?"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als contactes del dispositiu <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"Vols permetre que la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la ubicació del dispositiu?"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la ubicació del dispositiu?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la ubicació del dispositiu <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'aplicació només tindrà accés a la ubicació quan l\'estiguis utilitzant"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la ubicació del dispositiu?"</string> @@ -669,16 +671,25 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"S\'hi va accedir ahir a les <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"S\'hi ha accedit el dia <xliff:g id="TIME_DATE_0">%1$s</xliff:g> a les <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"La contrasenya d\'un sol ús és 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permet la configuració restringida"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opció de configuració restringida"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per a la teva seguretat, aquesta opció de configuració no està disponible en aquests moments."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acció no disponible durant les trucades"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No es permet que les aplicacions instal·lin altres aplicacions durant una trucada.\n\n Els estafadors solen demanar aquest tipus d\'acció durant les trucades, de manera que s\'ha bloquejat per protegir-te. Si algú que no coneixes t\'indica que facis aquesta acció, pot ser que es tracti d\'una estafa."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A l\'aplicació se li ha denegat l\'accés a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'aplicació ha demanat accés a un permís sensible, el qual pot posar en risc la teva informació personal o financera.<xliff:g id="ID_1"><br><br></xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís restringit. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Més informació sobre com pots permetre l\'accés</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A l\'aplicació se li ha denegat l\'accés per ser <xliff:g id="ROLE_NAME">%1$s</xliff:g> de manera predeterminada"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'aplicació ha demanat accés a permisos sensibles, els quals poden posar en risc la teva informació personal o financera.<xliff:g id="ID_1"><br><br></xliff:g>És possible que l\'aplicació no funcioni correctament sense aquests permisos restringits. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Més informació sobre com pots permetre l\'accés</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"A l\'aplicació se li ha denegat l\'accés"</string> - <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accés a aquest permís pot posar en risc la teva informació personal i financera.<xliff:g id="ID_1"><br><br></xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Més informació sobre com pots permetre l\'accés</a>"</string> + <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accés a aquest permís pot posar en risc la teva informació personal i financera.<xliff:g id="ID_1"><br><br></xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís restringit. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Més informació sobre com pots permetre l\'accés</a>"</string> <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Més informació"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"D\'acord"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"S\'ha suprimit la sol·licitud de permís"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió d\'estríming. Primer concedeix el permís al teu telèfon."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Per a trucades o missatges de text d\'emergència"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"La ubicació s\'ha enviat als serveis d\'emergències"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aquesta aplicació ha accedit a la ubicació del teu dispositiu mentre feies una trucada o enviaves un missatge de text a un número d\'emergència. Això pot passar fins i tot quan l\'aplicació no té permís d\'ubicació o la ubicació del dispositiu està desactivada. "<a href="https://support.google.com/android/answer/9319337">"Més informació"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Comprova el telèfon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"No s\'ha pogut contactar amb el telèfon"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vols tornar-ho a provar?"</string> </resources> diff --git a/PermissionController/res/values-cs-v35/strings.xml b/PermissionController/res/values-cs-v35/strings.xml new file mode 100644 index 000000000..95aa40cc4 --- /dev/null +++ b/PermissionController/res/values-cs-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Zabezpečení"</string> +</resources> diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml index 90b265f1c..95eb6a558 100644 --- a/PermissionController/res/values-cs/strings.xml +++ b/PermissionController/res/values-cs/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"oprávnění"</string> <string name="cancel" msgid="8943320028373963831">"Zrušit"</string> <string name="back" msgid="6249950659061523680">"Zpět"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zavřít"</string> <string name="available" msgid="6007778121920339498">"Dostupné"</string> <string name="blocked" msgid="9195547604866033708">"Blokováno"</string> <string name="on" msgid="280241003226755921">"Zapnuto"</string> @@ -440,10 +441,11 @@ <string name="default_app_none" msgid="9084592086808194457">"Žádná aplikace"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Výchozí nastavení systému)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Žádné aplikace"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Ostatní služby NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Vybráno"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Vybráno – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> - <string name="special_app_access_search_keyword" msgid="8032347212290774210">"zvláštní přístup aplikací"</string> - <string name="special_app_access" msgid="5019319067120213797">"Přístupy pro aplikace"</string> + <string name="special_app_access_search_keyword" msgid="8032347212290774210">"speciální přístup aplikací"</string> + <string name="special_app_access" msgid="5019319067120213797">"Speciální přístup aplikací"</string> <string name="no_special_app_access" msgid="6950277571805106247">"Žádný přístup ke spec. aplik."</string> <string name="special_app_access_no_apps" msgid="4102911722787886970">"Žádné aplikace"</string> <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Nepodporuje pracovní profil"</string> @@ -550,9 +552,7 @@ <string name="privdash_label_none" msgid="5991866260360484858">"Žádné"</string> <string name="privdash_label_24h" msgid="1512532123865375319">"Posledních\n24 hodin"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"Posledních\n7 dní"</string> - <!-- String.format failed for translation --> - <!-- no translation found for privdash_usage_percent (6893824766124414127) --> - <skip /> + <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> %%"</string> <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je chráněna Androidem. Protože se vaše data zpracovávají na tomto zařízení, oprávnění používaná touto aplikací se nezobrazují na stavovém řádku ani na panelu ochrany soukromí."</string> <string name="exempt_info_label" msgid="6286190981253476699">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je chráněna Androidem. Protože se vaše data zpracovávají na tomto zařízení, oprávnění používaná touto aplikací se nezobrazují na panelu ochrany soukromí."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"Fotoaparát zařízení je blokován"</string> @@ -671,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Poslední přístup: včera v <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Poslední přístup: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Vaše jednorázové heslo je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Povolit omezená nastavení"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Blokované nastavení"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z důvodu vaší bezpečnosti toto nastavení momentálně není dostupné."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Akce není během telefonního hovoru k dispozici"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Během telefonního hovoru nelze aplikacím povolit instalovat další aplikace.\n\n O tento typ akce během hovorů často žádají podvodníci. Kvůli vaší ochraně je proto blokována. Pokud vás k této akci navádí někdo, koho neznáte, může se jednat o podvod."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaci byl odepřen přístup k oprávnění <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikace požádala o přístup k citlivému oprávnění, které může ohrozit vaše osobní a finanční údaje.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že aplikace bez tohoto oprávnění nebude fungovat správně. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Jak povolit přístup</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaci byla odepřena role výchozí <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -683,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Žádost o oprávnění byla potlačena"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Tato aplikace požaduje další oprávnění, která ale nelze udělit během relace streamování. Oprávnění udělte nejprve na telefonu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pro tísňové volání nebo textovou zprávu"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Poloha byla odeslána na tísňovou linku"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Tato aplikace získala přístup k poloze vašeho zařízení během hovoru nebo při posílání SMS na číslo tísňového volání. K tomu může dojít, i když aplikace nemá oprávnění pro přístup k poloze nebo je poloha zařízení vypnutá. "<a href="https://support.google.com/android/answer/9319337">"Další informace"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Zkontrolujte svůj telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"K telefonu se nepodařilo připojit"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Zkusit znovu?"</string> </resources> diff --git a/PermissionController/res/values-da-v35/strings.xml b/PermissionController/res/values-da-v35/strings.xml new file mode 100644 index 000000000..911941e5b --- /dev/null +++ b/PermissionController/res/values-da-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sikkerhed"</string> +</resources> diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml index d8af25f0d..8f7acd378 100644 --- a/PermissionController/res/values-da/strings.xml +++ b/PermissionController/res/values-da/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"tilladelser"</string> <string name="cancel" msgid="8943320028373963831">"Annuller"</string> <string name="back" msgid="6249950659061523680">"Tilbage"</string> + <string name="dialog_close" msgid="6840699812532384661">"Luk"</string> <string name="available" msgid="6007778121920339498">"Tilgængelig"</string> <string name="blocked" msgid="9195547604866033708">"Blokeret"</string> <string name="on" msgid="280241003226755921">"Til"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ingen"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ingen apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andre NFC-tjenester"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valgt"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valgt – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"særlig appadgang"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> vil gerne uploade fejretningsoplysninger."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Vil du dele fejlretningsdata?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Systemet har registreret et problem."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> anmoder om at uploade en fejlrapport fra denne enhed, som stammer fra <xliff:g id="DATE">%2$s</xliff:g> kl. <xliff:g id="TIME">%3$s</xliff:g>. Fejlrapporter indeholder personlige oplysninger om din enhed eller registreres af apps, f.eks. brugernavne, lokationsdata, enheds-id\'er og netværksoplysninger. Oplysningerne i fejlrapporterne må kun deles med personer og apps, du har tillid til. Vil du give <xliff:g id="APP_NAME_1">%4$s</xliff:g> tilladelse til at uploade en fejlrapport?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> anmoder om at uploade en fejlrapport fra denne enhed, som stammer fra <xliff:g id="DATE">%2$s</xliff:g> kl. <xliff:g id="TIME">%3$s</xliff:g>. Fejlrapporter indeholder personlige oplysninger om din enhed, eller som er registreret af apps, f.eks. brugernavne, lokationsdata, enheds-id\'er og netværksoplysninger. Oplysningerne i fejlrapporterne må kun deles med personer og apps, du har tillid til. Vil du give <xliff:g id="APP_NAME_1">%4$s</xliff:g> tilladelse til at uploade en fejlrapport?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Der opstod en fejl i behandlingen af fejlrapporten for <xliff:g id="APP_NAME">%1$s</xliff:g>. Det er derfor ikke muligt at dele detaljerede fejlretningsdata. Vi beklager ulejligheden."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Tillad"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Afvis"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Tilgået i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Tilgået <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Din engangskode er 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Tillad begrænsede indstillinger"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrænset indstilling"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Af hensyn til din sikkerhed er denne indstilling i øjeblikket ikke tilgængelig."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handlingen er utilgængelig under et telefonopkald"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det er ikke tilladt at give apps tilladelse til at installere andre apps under et telefonopkald.\n\n Svindlere opfordrer ofte til denne type handling under telefonopkald, så den er blokeret for at beskytte dig. Hvis du bliver bedt om at foretage denne handling af en person, du ikke kender, kan det være svindel."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen blev nægtet adgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen anmodede om adgang til en følsom tilladelse, der kan kompromittere dine personlige eller økonomiske oplysninger.<xliff:g id="ID_1"><br><br></xliff:g>Appen fungerer muligvis ikke korrekt uden denne begrænsede tilladelse. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Se, hvordan du giver tilladelse</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen blev nægtet adgang til at være <xliff:g id="ROLE_NAME">%1$s</xliff:g> som standard"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Anmodning om tilladelse blokeret"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Denne app anmoder om yderligere tilladelser, men der kan ikke gives tilladelser under en streamingsession. Giv tilladelse på din telefon først."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For opkald eller beskeder til et alarmnummer"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokation sendt til nødtjenester"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Denne app har tilgået din enheds lokation i forbindelse med et opkald eller en besked til et alarmnummer. Dette kan lade sig gøre, selvom appen ikke har lokationstilladelse, eller enhedens lokation er deaktiveret. "<a href="https://support.google.com/android/answer/9319337">"Få flere oplysninger"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Tjek din telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Der kunne ikke oprettes forbindelse til telefonen"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vil du prøve igen?"</string> </resources> diff --git a/PermissionController/res/values-de-v35/strings.xml b/PermissionController/res/values-de-v35/strings.xml new file mode 100644 index 000000000..7f04a8b8c --- /dev/null +++ b/PermissionController/res/values-de-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sicherheit"</string> +</resources> diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml index a1347a736..b34776c76 100644 --- a/PermissionController/res/values-de/strings.xml +++ b/PermissionController/res/values-de/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"Berechtigungen"</string> <string name="cancel" msgid="8943320028373963831">"Abbrechen"</string> <string name="back" msgid="6249950659061523680">"Zurück"</string> + <string name="dialog_close" msgid="6840699812532384661">"Schließen"</string> <string name="available" msgid="6007778121920339498">"Verfügbar"</string> <string name="blocked" msgid="9195547604866033708">"Gesperrt"</string> <string name="on" msgid="280241003226755921">"An"</string> @@ -369,7 +370,7 @@ <string name="role_sms_label" msgid="8456999857547686640">"Standard-SMS-App"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"SMS-App"</string> <string name="role_sms_description" msgid="3424020199148153513">"Apps, mit denen du über deine Telefonnummer unter anderem SMS, Fotos oder Videos senden und empfangen kannst"</string> - <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g>als Standard-SMS-App festlegen?"</string> + <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g> als Standard-SMS-App festlegen?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Diese App erhält Zugriff auf Folgendes: Kamera, Kontakte, Mikrofon, Dateien und Medien, Telefon und SMS"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"textnachricht, sms, sms schicken, sms senden, nachrichten, mms"</string> <string name="role_emergency_label" msgid="7028825857206842366">"Standardmäßige Notfall-App"</string> @@ -418,7 +419,7 @@ <string name="phone_call_uses_microphone_and_camera" msgid="6291898755681748189">"Kamera und Mikrofon werden für <b>Videoanruf</b> verwendet"</string> <string name="phone_call_uses_camera" msgid="2048417022147857418">"Kamera wird für <b>Videoanruf</b> verwendet"</string> <string name="system_uses_microphone" msgid="576672130318877143">"Der Zugriff auf das Mikrofon erfolgt über den Systemdienst"</string> - <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Der Zugriff auf die Kamera und das Mikrofon erfolt über den Systemdienst"</string> + <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Der Zugriff auf die Kamera und das Mikrofon erfolgt über den Systemdienst"</string> <string name="system_uses_camera" msgid="1911223105234441470">"Der Zugriff auf die Kamera erfolgt über den Systemdienst"</string> <string name="other_use" msgid="6564855051022776692">"Andere Funktionen:"</string> <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"Ok"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Keine"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System-Standardeinstellung)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Keine Apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andere NFC‑Dienste"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Ausgewählt"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Ausgewählt – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"spezieller App-Zugriff"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> möchte Informationen zur Fehlerbehebung hochladen."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Daten zur Fehlerbehebung teilen?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Das System hat ein Problem erkannt."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> möchte einen Fehlerbericht von diesem Gerät hochladen, der am <xliff:g id="DATE">%2$s</xliff:g> um <xliff:g id="TIME">%3$s</xliff:g> erstellt wurde. Fehlerberichte enthalten Informationen zu deinem Gerät, die du persönlich eingegeben hast oder die von Apps aufgezeichnet werden, z. B. Nutzernamen, Standortdaten, Geräte-IDs und Netzwerkinformationen. Teile Fehlerberichte nur mit Personen und Apps, denen du vertraust. Darf <xliff:g id="APP_NAME_1">%4$s</xliff:g> einen Fehlerbericht hochladen?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> möchte einen Fehlerbericht von diesem Gerät hochladen, der am <xliff:g id="DATE">%2$s</xliff:g> um <xliff:g id="TIME">%3$s</xliff:g> erstellt wurde. Fehlerberichte enthalten Informationen zu deinem Gerät, die du persönlich eingegeben hast oder die von Apps aufgezeichnet werden, z. B. Nutzernamen, Standortdaten, Geräte-IDs und Netzwerkinformationen. Teile Fehlerberichte nur mit Personen und Apps, denen du vertraust. Darf <xliff:g id="APP_NAME_1">%4$s</xliff:g> einen Fehlerbericht hochladen?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Bei der Verarbeitung des Fehlerberichts für <xliff:g id="APP_NAME">%1$s</xliff:g> ist ein Fehler aufgetreten. Die detaillierten Daten zur Fehlerbehebung wurden daher nicht geteilt. Wir entschuldigen uns für die Störung."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Zulassen"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Ablehnen"</string> @@ -550,9 +552,7 @@ <string name="privdash_label_none" msgid="5991866260360484858">"Keine"</string> <string name="privdash_label_24h" msgid="1512532123865375319">"Letzte\n24 Stunden"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"Letzte\n7 Tage"</string> - <!-- String.format failed for translation --> - <!-- no translation found for privdash_usage_percent (6893824766124414127) --> - <skip /> + <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> %%"</string> <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist durch Android geschützt. Weil deine Daten direkt auf diesem Gerät verarbeitet werden, wird weder in der Statusleiste noch auf deinem Privatsphäredashboard die Nutzung der Berechtigungen durch diese App angezeigt."</string> <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist durch Android geschützt. Weil deine Daten direkt auf diesem Gerät verarbeitet werden, wird auf deinem Privatsphäredashboard die Nutzung der Berechtigungen durch diese App nicht angezeigt."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"Gerätekamera ist blockiert"</string> @@ -671,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Zugriff: gestern, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Zugriff: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Dein Einmalpasswort lautet 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Eingeschränkte Einstellungen zulassen"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Eingeschränkte Einstellung"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Aus Sicherheitsgründen ist diese Einstellung derzeit nicht verfügbar."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Aktion während Anrufen nicht verfügbar"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Während eines Anrufs kannst du Apps nicht erlauben, andere Apps zu installieren.\n\n Betrüger versuchen oft, dich am Telefon genau dazu zu bringen, daher haben wir diese Art von Aktion zu deinem Schutz blockiert. Drängt dich jemand Unbekanntes zu einer solchen Installation, könnte es sich um Betrug handeln."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App wurde Zugriff auf „<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>“ verweigert"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die App hat Zugriff auf eine vertrauliche Berechtigung angefordert. Wenn du diesen zulässt, sind deine privaten Daten und Finanzdaten eventuell gefährdet.<xliff:g id="ID_1"><br><br></xliff:g>Die App funktioniert ohne diese eingeschränkte Berechtigung aber möglicherweise nicht richtig. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Informationen dazu, wie du den Zugriff erlaubst</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App wurde der Zugriff verweigert, die standardmäßige <xliff:g id="ROLE_NAME">%1$s</xliff:g> zu sein"</string> @@ -683,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ok"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Berechtigungsanfrage unterdrückt"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Diese App fordert zusätzliche Berechtigungen an – in einer Streamingsitzung können jedoch keine Berechtigungen gewährt werden. Erteile zuerst die Berechtigung auf deinem Smartphone."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Für Notrufe oder Nachrichten an eine Notrufnummer"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Standort wurde an den Rettungsdienst gesendet"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Diese App hat den Standort deines Geräts abgerufen, während ein Notruf getätigt oder eine Nachricht an eine Notrufnummer gesendet wurde. Das kann auch dann passieren, wenn die App keine Berechtigung zur Standortermittlung hat oder der Gerätestandort deaktiviert ist. "<a href="https://support.google.com/android/answer/9319337">"Weitere Informationen"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Smartphone prüfen"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Verbindung mit dem Smartphone nicht möglich"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Noch einmal versuchen?"</string> </resources> diff --git a/PermissionController/res/values-el-v35/strings.xml b/PermissionController/res/values-el-v35/strings.xml new file mode 100644 index 000000000..fa7c62da3 --- /dev/null +++ b/PermissionController/res/values-el-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Ασφάλεια"</string> +</resources> diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml index fe7103b90..ed69ea315 100644 --- a/PermissionController/res/values-el/strings.xml +++ b/PermissionController/res/values-el/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"άδειες"</string> <string name="cancel" msgid="8943320028373963831">"Ακύρωση"</string> <string name="back" msgid="6249950659061523680">"Πίσω"</string> + <string name="dialog_close" msgid="6840699812532384661">"Κλείσιμο"</string> <string name="available" msgid="6007778121920339498">"Διαθέσιμο"</string> <string name="blocked" msgid="9195547604866033708">"Αποκλεισμένο"</string> <string name="on" msgid="280241003226755921">"Ενεργό"</string> @@ -110,7 +111,7 @@ <!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) --> <!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) --> <string name="permission_access_always" msgid="1474641821883823446">"Να επιτρέπεται πάντα"</string> - <string name="permission_access_only_foreground" msgid="7801170728159326195">"Μόνο με τη χρήση της εφαρμογής"</string> + <string name="permission_access_only_foreground" msgid="7801170728159326195">"Να επιτρέπεται μόνο κατά τη χρήση της εφαρμογής"</string> <string name="permission_access_never" msgid="4647014230217936900">"Να μην επιτρέπεται"</string> <string name="loading" msgid="4789365003890741082">"Φόρτωση…"</string> <string name="all_permissions" msgid="6911125611996872522">"Όλες οι άδειες"</string> @@ -187,9 +188,9 @@ <string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"Να επιτρέπεται η διαχείριση όλων των αρχείων"</string> <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_foreground" msgid="1991570451498943207">"Να επιτρέπεται μόνο κατά τη χρήση της εφαρμογής"</string> <string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Να επιτρέπονται πάντα όλα"</string> - <string name="app_permission_button_ask" msgid="3342950658789427">"Να ερωτώμαι κάθε φορά"</string> + <string name="app_permission_button_ask" msgid="3342950658789427">"Ερώτηση κάθε φορά"</string> <string name="app_permission_button_deny" msgid="6016454069832050300">"Να μην επιτρέπεται"</string> <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Να επιτρέπεται περιορισμένη πρόσβαση"</string> <string name="precise_image_description" msgid="6349638632303619872">"Ακριβής τοποθεσία"</string> @@ -255,7 +256,7 @@ <string name="allowed_storage_scoped" msgid="5383645873719086975">"Επιτρέπεται η πρόσβαση μόνο σε μέσα"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Επιτρέπεται η διαχείριση όλων των αρχείων"</string> <string name="ask_header" msgid="2633816846459944376">"Ερώτηση κάθε φορά"</string> - <string name="denied_header" msgid="903209608358177654">"Δεν επιτρέπεται"</string> + <string name="denied_header" msgid="903209608358177654">"Δεν επιτρέπονται"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> στη συσκευή <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Δείτε περισσότερες εφαρμογές με πρόσβαση σε όλα τα αρχεία"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{1 ημέρα}other{# ημέρες}}"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string> <string name="role_home_label" msgid="3871847846649769412">"Προεπ. εφαρμογή αρχικής οθόνης"</string> <string name="role_home_short_label" msgid="8544733747952272337">"Εφαρμογή αρχικής οθόνης"</string> - <string name="role_home_description" msgid="7997371519626556675">"Εφαρμογές, που συχνά αποκαλούνται εφαρμογές εκκίνησης, που αντικαθιστούν τις Αρχικές οθόνες στη συσκευή σας Android και σας δίνουν πρόσβαση στα περιεχόμενα και τις λειτουργίες της συσκευή σας"</string> + <string name="role_home_description" msgid="7997371519626556675">"Εφαρμογές, που συχνά αποκαλούνται εφαρμογές εκκίνησης, που αντικαθιστούν τις Αρχικές οθόνες στη συσκευή σας Android και σας δίνουν πρόσβαση στα περιεχόμενα και τις λειτουργίες της συσκευής σας"</string> <string name="role_home_request_title" msgid="738136983453341081">"Ορισμός εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> ως προεπιλεγμένης εφαρμογής αρχικής οθόνης;"</string> <string name="role_home_request_description" msgid="2658833966716057673">"Δεν απαιτούνται άδειες"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"εφαρμογή εκκίνησης"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Καμία"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Προεπιλογή συστήματος)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Δεν υπάρχουν εφαρμογές"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Άλλες υπηρεσίες NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Επιλέχτηκε"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Επιλέχτηκε - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ειδική πρόσβαση στην εφαρμογή"</string> @@ -469,7 +471,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε φωτογραφίες και μέσα στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> να έχει πρόσβαση στις επαφές σας;"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στις επαφές σας στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στην τοποθεσία της συσκευής <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s</b>;"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Η εφαρμογή θα έχει πρόσβαση στην τοποθεσία μόνο κατά τη διάρκεια χρήσης της εφαρμογής"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string> @@ -497,15 +499,15 @@ <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_device_aware_read_media_aural" msgid="7927884506238101064">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε μουσική και σε ήχο στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$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_read_media_visual" msgid="5548780620779729975">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση στις φωτογραφίες και τα βίντεο αυτής της συσκευής;"</string> <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε φωτογραφίες και σε βίντεο στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> <string name="permgrouprequest_more_photos" msgid="128933814654231321">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε περισσότερες φωτογραφίες και βίντεο αυτής της συσκευής;"</string> <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η πρόσβαση σε περισσότερες φωτογραφίες και βίντεο στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η εγγραφή ήχου στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η ηχογράφηση στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$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> - <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η εγγραφή ήχου στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> + <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Να επιτρέπεται στο <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η ηχογράφηση;"</string> + <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> η ηχογράφηση στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Αυτή η εφαρμογή ενδέχεται να εγγράφει βίντεο συνεχώς, ακόμη και όταν δεν τη χρησιμοποιείτε. "<annotation id="link">"Έγκριση στις ρυθμίσεις."</annotation></string> <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Αλλαγή πρόσβασης στο μικρόφωνο για την εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>;"</string> <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Να γίνει αλλαγή της πρόσβασης μικροφώνου για την εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> στη συσκευή <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>;"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Πρόσβαση χθες, στις <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Πρόσβαση στις <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ο κωδικός πρόσβασης μίας χρήσης είναι 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Επιτρέπονται περιορισμένες ρυθμίσεις"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Περιορισμένη ρύθμιση"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Για την ασφάλειά σας, αυτή η ρύθμιση δεν είναι διαθέσιμη αυτή τη στιγμή."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Δεν διατίθεται κατά τη διάρκεια τηλεφωνικής κλήσης"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Κατά τη διάρκεια μιας τηλεφωνικής κλήσης, δεν επιτρέπεται η παραχώρηση άδειας σε μια εφαρμογή για εγκατάσταση άλλων εφαρμογών.\n\n Οι απατεώνες συχνά ζητούν να κάνετε τέτοιες ενέργειες κατά τη διάρκεια τηλεφωνικών κλήσεων, επομένως η ενέργεια έχει αποκλειστεί για την προστασία σας. Αν κάποιος που δεν γνωρίζετε σας ζητάει να πραγματοποιήσετε αυτή την ενέργεια, ενδέχεται να πρόκειται για απάτη."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Απορρίφθηκε η πρόσβαση της εφαρμογής στην άδεια <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Η εφαρμογή ζήτησε πρόσβαση σε μια άδεια πρόσβασης σε ευαίσθητες πληροφορίες, γεγονός που μπορεί να θέσει σε κίνδυνο τα προσωπικά και οικονομικά στοιχεία σας.<xliff:g id="ID_1"><br><br></xliff:g>Είναι πιθανό η εφαρμογή να μην λειτουργεί σωστά χωρίς αυτή την περιορισμένη άδεια. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Μάθετε πώς μπορείτε να επιτρέψετε την πρόσβαση</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Δεν επιτράπηκε στην εφαρμογή να οριστεί ως η προεπιλεγμένη <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ΟΚ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Το αίτημα άδειας ανεστάλη"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Αυτή η εφαρμογή ζητά πρόσθετες άδειες, αλλά οι άδειες δεν μπορούν να εκχωρηθούν σε μια περίοδο σύνδεσης ροής. Εκχωρήστε πρώτα την άδεια στο τηλέφωνό σας."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Για κλήσεις ή μηνύματα έκτακτης ανάγκης"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Η τοποθεσία στάλθηκε στις υπηρεσίες έκτακτης ανάγκης"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Αυτή η εφαρμογή απέκτησε πρόσβαση στην τοποθεσία της συσκευής σας κατά τη διάρκεια μιας κλήσης ή κατά την αποστολή ενός μηνύματος σε έναν αριθμό έκτακτης ανάγκης. Αυτό μπορεί να συμβεί ακόμα και εάν η εφαρμογή δεν έχει άδεια τοποθεσίας ή εάν η τοποθεσία της συσκευής είναι απενεργοποιημένη. "<a href="https://support.google.com/android/answer/9319337">"Μάθετε περισσότερα"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Ελέγξτε το τηλέφωνό σας"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Δεν ήταν δυνατή η επικοινωνία με το τηλέφωνό σας"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Επανάληψη;"</string> </resources> diff --git a/PermissionController/res/values-en-rAU-v35/strings.xml b/PermissionController/res/values-en-rAU-v35/strings.xml new file mode 100644 index 000000000..c4b7df49d --- /dev/null +++ b/PermissionController/res/values-en-rAU-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Security"</string> +</resources> diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml index 5ae4af174..d5a94de2f 100644 --- a/PermissionController/res/values-en-rAU/strings.xml +++ b/PermissionController/res/values-en-rAU/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string> <string name="cancel" msgid="8943320028373963831">"Cancel"</string> <string name="back" msgid="6249950659061523680">"Back"</string> + <string name="dialog_close" msgid="6840699812532384661">"Close"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Blocked"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"None"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Other NFC services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1"><br><br></xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Learn how to allow access</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check your phone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Couldn\'t reach your phone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Retry?"</string> </resources> diff --git a/PermissionController/res/values-en-rCA-v35/strings.xml b/PermissionController/res/values-en-rCA-v35/strings.xml new file mode 100644 index 000000000..c4b7df49d --- /dev/null +++ b/PermissionController/res/values-en-rCA-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Security"</string> +</resources> diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml index 972d1e409..d39bcf96a 100644 --- a/PermissionController/res/values-en-rCA/strings.xml +++ b/PermissionController/res/values-en-rCA/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string> <string name="cancel" msgid="8943320028373963831">"Cancel"</string> <string name="back" msgid="6249950659061523680">"Back"</string> + <string name="dialog_close" msgid="6840699812532384661">"Close"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Blocked"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"None"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Other NFC services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Your one time password is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it’s blocked to protect you. If you are being guided to take this action by someone you don’t know, it might be a scam."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1"><br><br></xliff:g>It\'s possible the app won\'t work properly without this restricted permission. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Learn how to allow access</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check your phone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Couldn\'t reach your phone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Retry?"</string> </resources> diff --git a/PermissionController/res/values-en-rGB-v35/strings.xml b/PermissionController/res/values-en-rGB-v35/strings.xml new file mode 100644 index 000000000..c4b7df49d --- /dev/null +++ b/PermissionController/res/values-en-rGB-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Security"</string> +</resources> diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml index db98df25c..f9a015705 100644 --- a/PermissionController/res/values-en-rGB/strings.xml +++ b/PermissionController/res/values-en-rGB/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string> <string name="cancel" msgid="8943320028373963831">"Cancel"</string> <string name="back" msgid="6249950659061523680">"Back"</string> + <string name="dialog_close" msgid="6840699812532384661">"Close"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Blocked"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"None"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Other NFC services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1"><br><br></xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Learn how to allow access</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check your phone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Couldn\'t reach your phone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Retry?"</string> </resources> diff --git a/PermissionController/res/values-en-rIN-v35/strings.xml b/PermissionController/res/values-en-rIN-v35/strings.xml new file mode 100644 index 000000000..c4b7df49d --- /dev/null +++ b/PermissionController/res/values-en-rIN-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Security"</string> +</resources> diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml index db98df25c..f9a015705 100644 --- a/PermissionController/res/values-en-rIN/strings.xml +++ b/PermissionController/res/values-en-rIN/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string> <string name="cancel" msgid="8943320028373963831">"Cancel"</string> <string name="back" msgid="6249950659061523680">"Back"</string> + <string name="dialog_close" msgid="6840699812532384661">"Close"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Blocked"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"None"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Other NFC services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action not available while on a phone call"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Allowing apps to install other apps is not allowed during a phone call.\n\n Scammers often request this type of action during phone call conversations, so it\'s blocked to protect you. If you are being guided to take this action by someone whom you don\'t know, it might be a scam."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1"><br><br></xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Learn how to allow access</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check your phone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Couldn\'t reach your phone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Retry?"</string> </resources> diff --git a/PermissionController/res/values-en-rXC-v35/strings.xml b/PermissionController/res/values-en-rXC-v35/strings.xml new file mode 100644 index 000000000..642f2d7e1 --- /dev/null +++ b/PermissionController/res/values-en-rXC-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Security"</string> +</resources> diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml index bd8edd323..33f7caeb9 100644 --- a/PermissionController/res/values-en-rXC/strings.xml +++ b/PermissionController/res/values-en-rXC/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string> <string name="cancel" msgid="8943320028373963831">"Cancel"</string> <string name="back" msgid="6249950659061523680">"Back"</string> + <string name="dialog_close" msgid="6840699812532384661">"Close"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Blocked"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"None"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Other NFC services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string> @@ -669,6 +671,7 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Your one time password is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Allow restricted settings"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> @@ -681,4 +684,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a>""</string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check your phone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Couldn\'t reach your phone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Retry?"</string> </resources> diff --git a/PermissionController/res/values-es-rUS-v35/strings.xml b/PermissionController/res/values-es-rUS-v35/strings.xml new file mode 100644 index 000000000..ecd01df78 --- /dev/null +++ b/PermissionController/res/values-es-rUS-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Seguridad"</string> +</resources> diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml index a3c034169..4041247fe 100644 --- a/PermissionController/res/values-es-rUS/strings.xml +++ b/PermissionController/res/values-es-rUS/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Atrás"</string> + <string name="dialog_close" msgid="6840699812532384661">"Cerrar"</string> <string name="available" msgid="6007778121920339498">"Disponible"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Activada"</string> @@ -70,7 +71,7 @@ <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Administra el acceso a datos del calendario, llamadas y mucho más"</string> <string name="granted_permission_decision" msgid="7824827491551861365">"Le diste a <xliff:g id="APP_NAME">%1$s</xliff:g> acceso a <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> <string name="denied_permission_decision" msgid="5308961501779563781">"Le negaste a <xliff:g id="APP_NAME">%1$s</xliff:g> el acceso a <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> - <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Hoy}=1{Hace 1 día}many{Hace # días}other{Hace # días}}"</string> + <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Hoy}=1{Hace 1 día}many{Hace # de días}other{Hace # días}}"</string> <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Inhabilitar app"</string> <string name="app_disable_dlg_text" msgid="3126943217146120240">"Si inhabilitas esta app, es posible que Android y otras apps ya no funcionen correctamente. Ten en cuenta que no puedes borrar esta app, ya que vino preinstalada en el dispositivo. Si la inhabilitas, se desactivará y se ocultará en tu dispositivo."</string> <string name="app_permission_manager" msgid="3903811137630909550">"Administrador de permisos"</string> @@ -139,15 +140,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="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_days" msgid="8238355545812998877">"{count,plural, =1{# día}many{# de días}other{# días}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# hora}many{# de 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_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="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Último día (#)}many{Últimos # de días}other{Últimos # días}}"</string> + <string name="permission_usage_last_n_hours" msgid="8490466053680267858">"{count,plural, =1{Última hora (#)}many{Últimas # de horas}other{Últimas # horas}}"</string> + <string name="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Último minuto (#)}many{Últimos # de 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,9 +162,9 @@ <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_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_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 # de 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 # de 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 # 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> <string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Quitar filtro"</string> @@ -258,17 +259,17 @@ <string name="denied_header" msgid="903209608358177654">"Sin permiso"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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="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="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# de días}other{# días}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# de horas}other{# horas}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# de minutos}other{# minutos}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# segundo}many{# de 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> <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Se quitaron los permisos para proteger tu privacidad. Presiona para revisar el cambio"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Se quitaron los permisos de las apps que no se usan"</string> <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Hace varios meses que no usas algunas apps. Presiona para verlas."</string> - <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app en desuso}many{# apps en desuso}other{# apps en desuso}}"</string> + <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app en desuso}many{# de apps en desuso}other{# apps en desuso}}"</string> <string name="unused_apps_notification_content" msgid="9195026773244581246">"Se quitaron los permisos y archivos temporales, y se detuvieron las notificaciones. Presiona para revisar estas opciones."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Revisar apps a las que se les quitaron permisos"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Se quitaron los permisos y los archivos temporales, y se detuvieron las notificaciones de las apps que no usaste durante un tiempo."</string> @@ -277,7 +278,7 @@ <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Mientras conducías, le otorgaste acceso a <xliff:g id="APP">%1$s</xliff:g> a <xliff:g id="PERMISSION">%2$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Mientras conducías, le otorgaste acceso a <xliff:g id="APP">%1$s</xliff:g> a <xliff:g id="PERMISSION_1">%2$s</xliff:g> y <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Mientras conducías, le otorgaste <xliff:g id="COUNT">%1$d</xliff:g> permisos a <xliff:g id="APP">%2$s</xliff:g>"</string> - <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_0">%1$s</xliff:g> y otra app}many{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}other{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}}"</string> + <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_0">%1$s</xliff:g> y otra app}many{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # de apps}other{Mientras conducías, les otorgaste acceso a <xliff:g id="APP_1">%1$s</xliff:g> y otras # apps}}"</string> <string name="go_to_settings" msgid="1053735612211228335">"Ir a Configuración"</string> <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Hace varios meses que no usas algunas apps"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"Apps a las que se les quitaron permisos"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ninguna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Predeterminada del sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Sin apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Otros servicios de NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Seleccionada"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Se seleccionó: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Acceso especial de apps"</string> @@ -660,7 +662,7 @@ <string name="learn_about_data_sharing" msgid="4200480587079488045">"Más información sobre el uso compartido de datos"</string> <string name="shares_location_with_third_parties" msgid="2278051743742057767">"Tus datos de ubicación ahora se comparten con terceros"</string> <string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Tus datos de ubicación ahora se comparten con terceros para publicidad o marketing"</string> - <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Se actualizó en el último día}=1{Se actualizó en el último día}many{Se actualizó en los últimos # de días}other{Se actualizó en los últimos # días}}"</string> + <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Se actualizó en el último día}=1{Se actualizó en el último día}many{Se actualizó hace # de días}other{Se actualizó hace # días}}"</string> <string name="no_updates_at_this_time" msgid="9031085635689982935">"No hay actualizaciones por el momento"</string> <string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Actualizaciones del uso compartido de datos"</string> <string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algunas apps cambiaron la forma en que podrían compartir tus datos de ubicación"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: ayer a la(s) <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> a la(s) <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Tu contraseña de un solo uso es 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configuración restringida"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Parámetro restringido"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este parámetro de configuración no está disponible actualmente."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"La acción no está disponible durante una llamada telefónica"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No se permite que las apps instalen otras apps durante una llamada telefónica.\n\n Esto se debe a que los estafadores suelen solicitar este tipo de acciones durante conversaciones telefónicas. Si alguien que no conoces te indica que realices esta acción, es posible que se trate de una estafa."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A la app se le negó el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La app solicitó acceso a un permiso sensible, lo que puede poner en riesgo tu información financiera y personal.<xliff:g id="ID_1"><br><br></xliff:g>Es posible que la app no funcione como corresponde sin este permiso restringido. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Descubre cómo permitir el acceso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A la app se le negó el acceso para usarse como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Se rechazó la solicitud de permiso"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta app solicita permisos adicionales, pero estos no se pueden otorgar durante una sesión de transmisión. Primero, otorga el permiso en el teléfono."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para mensaje de texto o llamada de emergencia"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ubicación enviada a servicios de emergencia"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta app accedió a la ubicación de tu dispositivo cuando hacías una llamada o enviabas un mensaje de texto a un número de emergencia. Esto puede suceder incluso si no se le otorgó a la app el permiso de ubicación o si la ubicación del dispositivo está desactivada. "<a href="https://support.google.com/android/answer/9319337">"Más información"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Revisa el teléfono"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"No se pudo establecer conexión con el teléfono"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"¿Quieres volver a intentarlo?"</string> </resources> diff --git a/PermissionController/res/values-es-v33/strings.xml b/PermissionController/res/values-es-v33/strings.xml index 69b1ce6f5..0104103fe 100644 --- a/PermissionController/res/values-es-v33/strings.xml +++ b/PermissionController/res/values-es-v33/strings.xml @@ -29,7 +29,7 @@ <string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>."</string> <string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Más alertas"</string> <string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"Alertas ignoradas"</string> - <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Ampliar para ver una alerta más}many{Ampliar para ver # alertas más}other{Ampliar para ver # alertas más}}"</string> + <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Amplía para ver una alerta más}many{Amplía para ver # alertas más}other{Amplía para ver # alertas más}}"</string> <string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"Alerta. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string> <string name="safety_center_resolved_issue_fallback" msgid="8548932070610766651">"Acción completada"</string> <string name="safety_center_qs_status_summary" msgid="5193925895830451177">"Comprueba los ajustes que pueden proteger más tu dispositivo"</string> diff --git a/PermissionController/res/values-es-v35/strings.xml b/PermissionController/res/values-es-v35/strings.xml new file mode 100644 index 000000000..ecd01df78 --- /dev/null +++ b/PermissionController/res/values-es-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Seguridad"</string> +</resources> diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml index 088d9a761..6298bd83b 100644 --- a/PermissionController/res/values-es/strings.xml +++ b/PermissionController/res/values-es/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Atrás"</string> + <string name="dialog_close" msgid="6840699812532384661">"Cerrar"</string> <string name="available" msgid="6007778121920339498">"Disponible"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Activado"</string> @@ -249,13 +250,13 @@ <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Actualmente denegado / Último acceso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"No ha accedido nunca"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Denegado / Último acceso: Nunca"</string> - <string name="allowed_header" msgid="7769277978004790414">"Permitido"</string> - <string name="allowed_always_header" msgid="6455903312589013545">"Permitidas siempre"</string> + <string name="allowed_header" msgid="7769277978004790414">"Permitidas"</string> + <string name="allowed_always_header" msgid="6455903312589013545">"Con permiso siempre"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitidas solo mientras se usan"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Pueden acceder solo al contenido multimedia"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Pueden gestionar todos los archivos"</string> <string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string> - <string name="denied_header" msgid="903209608358177654">"No permitidas"</string> + <string name="denied_header" msgid="903209608358177654">"No permitido"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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> @@ -353,7 +354,7 @@ <string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> puede ver tu pantalla, lo que haces y lo que introduces; realizar acciones; y controlar la pantalla."</string> <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Estas aplicaciones pueden ver tu pantalla, lo que haces y lo que introduces; realizar acciones; y controlar la pantalla."</string> <string name="role_assistant_label" msgid="4727586018198208128">"Aplicación de asistente digital predeterminada"</string> - <string name="role_assistant_short_label" msgid="3369003713187703399">"Asistente digital"</string> + <string name="role_assistant_short_label" msgid="3369003713187703399">"Aplicación de asistente digital"</string> <string name="role_assistant_description" msgid="6622458130459922952">"Las aplicaciones de asistencia te ayudan según la información que aparezca en la pantalla. Algunas aplicaciones admiten tanto el menú de aplicaciones como los servicios de entrada de voz para ofrecerte asistencia integrada."</string> <string name="role_browser_label" msgid="2877796144554070207">"Aplicación de navegador predeterminada"</string> <string name="role_browser_short_label" msgid="6745009127123292296">"Aplicación de navegador"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ninguna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Predeterminado del sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"No hay aplicaciones"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Otros servicios de NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Seleccionada"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Seleccionada: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"acceso especial de aplicaciones"</string> @@ -582,7 +584,7 @@ <string name="privacy_controls_qs" msgid="5780144882040591169">"Tus controles de privacidad"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"Más ajustes"</string> <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Acceso a la cámara"</string> - <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"Acceso al micrófono"</string> + <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"Acceso a micro"</string> <string name="permissions_removed_qs" msgid="8957319130625294572">"Permiso retirado"</string> <string name="camera_usage_qs" msgid="4394233566086665994">"Ver el uso reciente de la cámara"</string> <string name="microphone_usage_qs" msgid="8527666682168170417">"Ver el uso reciente del micrófono"</string> @@ -656,7 +658,7 @@ <string name="data_sharing_updates_title" msgid="7996933386875213859">"Cambios en los datos compartidos de ubicación"</string> <string name="data_sharing_updates_summary" msgid="764113985772233889">"Consulta las aplicaciones que han cambiado cómo comparten tus datos de ubicación"</string> <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Estas aplicaciones han cambiado cómo pueden compartir tus datos de ubicación. Puede que no los hayan compartido antes, o que ahora lo hagan con fines publicitarios o de marketing."</string> - <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Los desarrolladores de estas aplicaciones han proporcionado información a una tienda de aplicaciones sobre sus formas de compartir datos. Es posible que la actualicen con el tiempo.\n\nLas formas en que se comparten los datos pueden variar en función de la versión de la aplicación, el modo en que la utilices, el país donde la uses y tu edad."</string> + <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Los desarrolladores de estas aplicaciones han proporcionado información a una tienda de aplicaciones sobre sus formas de compartir datos. Es posible que la actualicen con el tiempo.\n\nLas formas en que se comparten los datos pueden variar en función de la versión de la aplicación, el modo en que la utilices, la zona donde la uses y tu edad."</string> <string name="learn_about_data_sharing" msgid="4200480587079488045">"Más información sobre cómo se comparten los datos"</string> <string name="shares_location_with_third_parties" msgid="2278051743742057767">"Tus datos de ubicación ahora se comparten con terceros"</string> <string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Tus datos de ubicación ahora se comparten con terceros para fines de publicidad o marketing"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: ayer, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Tu contraseña de un solo uso es 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir ajustes restringidos"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ajuste restringido"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este ajuste no está disponible actualmente."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acción no disponible durante una llamada"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"No es posible permitir que las aplicaciones instalen otras aplicaciones durante una llamada telefónica.\n\n Los estafadores suelen solicitar este tipo de acciones durante las conversaciones telefónicas, por lo que esta acción se bloquea para protegerte. Si alguien que no conoces te indica que debes realizar esta acción, podría tratarse de una estafa."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Se ha denegado el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a la aplicación"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La aplicación ha solicitado acceso a un permiso sensible que puede poner en riesgo tu información personal y financiera.<xliff:g id="ID_1"><br><br></xliff:g>Es posible que la aplicación no funcione correctamente sin este permiso restringido. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Consulta cómo permitir el acceso</a>."</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Se ha denegado el acceso para que la aplicación sea <xliff:g id="ROLE_NAME">%1$s</xliff:g> predeterminada"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitud de permiso rechazada"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta aplicación está solicitando permisos adicionales, pero no se pueden dar durante una sesión de streaming. Da primero el permiso en tu teléfono."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para llamadas o mensajes de emergencia"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ubicación enviada a los servicios de emergencias"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta aplicación ha accedido a la ubicación de tu dispositivo mientras hacías una llamada o enviabas un mensaje al número de emergencia. Esto puede ocurrir aunque la aplicación no tenga el permiso de ubicación o la ubicación del dispositivo esté desactivada. "<a href="https://support.google.com/android/answer/9319337">"Más información"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Consulta tu teléfono"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"No se ha podido conectar con tu teléfono"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"¿Reintentar?"</string> </resources> diff --git a/PermissionController/res/values-et-v35/strings.xml b/PermissionController/res/values-et-v35/strings.xml new file mode 100644 index 000000000..3723766d0 --- /dev/null +++ b/PermissionController/res/values-et-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Turvalisus"</string> +</resources> diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml index 42780598e..a289032c0 100644 --- a/PermissionController/res/values-et/strings.xml +++ b/PermissionController/res/values-et/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"load"</string> <string name="cancel" msgid="8943320028373963831">"Tühista"</string> <string name="back" msgid="6249950659061523680">"Tagasi"</string> + <string name="dialog_close" msgid="6840699812532384661">"Sule"</string> <string name="available" msgid="6007778121920339498">"Saadaval"</string> <string name="blocked" msgid="9195547604866033708">"Blokeeritud"</string> <string name="on" msgid="280241003226755921">"Sees"</string> @@ -206,7 +207,7 @@ <string name="auto_revoke_label" msgid="5068393642936571656">"Eemalda load, kui rakendust ei kasutata"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Eemalda load ja vabasta ruumi"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"Kasutamata rakenduse tegevuste peatamine"</string> - <string name="unused_apps_label_v3" msgid="693340578642156657">"Halda kasutamata rakendusi"</string> + <string name="unused_apps_label_v3" msgid="693340578642156657">"Kasutamata rakenduste haldamine"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Eemaldatakse load, kustutatakse ajutised failid ja peatatakse märguanded"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Eemalda load, kustuta ajutised failid, peata märguanded ja arhiivi rakendus"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"Teie andmete kaitsmiseks eemaldatakse selle rakenduse load, kui seda mõne kuu jooksul ei kasutata."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Puudub"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Süsteemi vaikeseade)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Rakendusi pole"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Muud NFC teenused"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valitud"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valitud – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"rakenduse erijuurdepääs"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Juurde pääsetud eile <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Juurde pääsetud <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Teie ühekordne parool on 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Piiratud seadete lubamine"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Piiratud seade"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Teie turvalisuse huvides pole see seade praegu saadaval."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Toiming pole telefonikõne ajal saadaval"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefonikõne ajal pole lubatud anda rakendustele luba installida teisi rakendusi.\n\n Petised taotlevad telefonivestluse ajal sageli seda tüüpi toimingut, mistõttu on see teie kaitsmiseks blokeeritud. Kui keegi, keda te ei tea, suunab teid seda toimingut tegema, võib tegu olla pettusega."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Rakendusele ei antud luba <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Rakendus taotles tundlikku luba, mis võib teie isikuandmed ja finantsteabe ohtu seada.<xliff:g id="ID_1"><br><br></xliff:g>Võimalik, et rakendus ei tööta ilma selle piiratud loata korralikult. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>> Teave juurdepääsu andmise kohta</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Rakendusele ei antud luba olla vaikimisi <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Loataotlus peideti"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"See rakendus taotleb lisalube, kuid lube ei saa voogesituse seansis anda. Kõigepealt andke luba oma telefonile."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Hädaabikõne või hädaabinumbrile tekstsõnumi saatmise jaoks"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Asukoht saadeti hädaabiteenustele"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"See rakendus pääses hädaabinumbrile helistamise või sõnumi saatmise käigus juurde teie seadme asukohale. See võib juhtuda ka siis, kui rakendusel pole asukohale juurdepääsu luba või seadme asukoht on välja lülitatud. "<a href="https://support.google.com/android/answer/9319337">"Lisateave"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Kontrollige oma telefoni"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Teie telefoniga ei saanud ühendust"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Kas soovite uuesti proovida?"</string> </resources> diff --git a/PermissionController/res/values-eu-v35/strings.xml b/PermissionController/res/values-eu-v35/strings.xml new file mode 100644 index 000000000..63e64934c --- /dev/null +++ b/PermissionController/res/values-eu-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Segurtasuna"</string> +</resources> diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml index 7ddfe7440..73c1015b6 100644 --- a/PermissionController/res/values-eu/strings.xml +++ b/PermissionController/res/values-eu/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"baimenak"</string> <string name="cancel" msgid="8943320028373963831">"Utzi"</string> <string name="back" msgid="6249950659061523680">"Atzera"</string> + <string name="dialog_close" msgid="6840699812532384661">"Itxi"</string> <string name="available" msgid="6007778121920339498">"Baimenduta"</string> <string name="blocked" msgid="9195547604866033708">"Blokeatuta"</string> <string name="on" msgid="280241003226755921">"Aktibatuta"</string> @@ -59,7 +60,7 @@ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Eman fitxategi guztiak kudeatzeko baimena"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Eman multimedia-fitxategiak erabiltzeko baimena"</string> <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Aplikazioak"</string> - <string name="app_permissions" msgid="3369917736607944781">"Aplikazio-baimenak"</string> + <string name="app_permissions" msgid="3369917736607944781">"Aplikazio-baimenaU+2060k"</string> <string name="unused_apps" msgid="2058057455175955094">"Erabiltzen ez diren aplikazioak"</string> <string name="edit_photos_description" msgid="5540108003480078892">"Editatu aplikazio honetarako hautatutako argazkiak"</string> <string name="no_unused_apps" msgid="12809387670415295">"Ez dago erabiltzen ez duzun aplikaziorik"</string> @@ -77,7 +78,7 @@ <string name="never_ask_again" msgid="4728762438198560329">"Ez galdetu berriro"</string> <string name="no_permissions" msgid="3881676756371148563">"Ez dago baimenik"</string> <string name="additional_permissions" msgid="5801285469338873430">"Baimen gehigarriak"</string> - <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ireki aplikazioaren informazioa"</string> + <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ireki aplikazioari buruzko informazioa"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{Beste #}other{Beste #}}"</string> <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Android-en bertsio zaharrago baterako diseinatuta dago aplikazio hau. Baimena ukatzen baduzu, agian aurrerantzean ez du behar bezala funtzionatuko."</string> <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Android-en bertsio zaharrago baterako dago diseinatuta aplikazio hau. Baimena ematen baduzu, biltegi osoa erabiltzeko baimena emango da (argazkiak, musika, audioa eta bestelako fitxategiak atzitzekoa barne)."</string> @@ -172,7 +173,7 @@ <string name="filter_by_time" msgid="6667864816999691642">"Iragazi orduaren arabera"</string> <string name="sort_spinner_most_permissions" msgid="1704349738096822836">"Baimen gehien erabili dituztenak"</string> <string name="sort_spinner_most_accesses" msgid="5283913004357220161">"Erabilienak"</string> - <string name="sort_spinner_recent" msgid="7513845273076525203">"Azkenak"</string> + <string name="sort_spinner_recent" msgid="7513845273076525203">"Azkenaldikoak"</string> <string name="sort_by_app" msgid="4055799843051138087">"Ordenatu erabileraren arabera"</string> <string name="sort_by_time" msgid="5435045320002150456">"Ordenatu orduaren arabera"</string> <string name="item_separator" msgid="4030255389809224513">", "</string> @@ -249,20 +250,20 @@ <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Une honetan ukatuta / Azken sarbide-data: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Ez da erabili inoiz"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Ukatuta / Ez da erabili inoiz"</string> - <string name="allowed_header" msgid="7769277978004790414">"Baimenduta"</string> + <string name="allowed_header" msgid="7769277978004790414">"Baimendutakoak"</string> <string name="allowed_always_header" msgid="6455903312589013545">"Beti baimendutakoak"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"Erabili bitartean soilik baimendutakoak"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Multimedia-fitx. soilik erabiltzeko baimena dutenak"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Fitxategi guztiak kudeatzeko baimena dutenak"</string> <string name="ask_header" msgid="2633816846459944376">"Galdetu beti"</string> - <string name="denied_header" msgid="903209608358177654">"Baimendu gabekoak"</string> + <string name="denied_header" msgid="903209608358177654">"Baimendu gabe"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</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="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="permission_reminders" msgid="6528257957664832636">"Baimenen gogorarazpenak"</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> <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Baimenak kendu egin dira zure pribatutasuna babesteko. Sakatu berrikusteko."</string> @@ -278,7 +279,7 @@ <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Gidatu bitartean, <xliff:g id="PERMISSION_1">%2$s</xliff:g> eta <xliff:g id="PERMISSION_2">%3$s</xliff:g> erabiltzeko baimena eman diozu <xliff:g id="APP">%1$s</xliff:g> aplikazioari"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Gidatu bitartean, <xliff:g id="COUNT">%1$d</xliff:g> baimen eman dizkiozu <xliff:g id="APP">%2$s</xliff:g> aplikazioari"</string> <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Gidatu bitartean, baimenak eman dizkiezu <xliff:g id="APP_0">%1$s</xliff:g> eta beste # aplikaziori}other{Gidatu bitartean, baimenak eman dizkiezu <xliff:g id="APP_1">%1$s</xliff:g> eta beste # aplikaziori}}"</string> - <string name="go_to_settings" msgid="1053735612211228335">"Joan ezarpenetara"</string> + <string name="go_to_settings" msgid="1053735612211228335">"Joan Ezarpenak atalera"</string> <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Aplikazio batzuk ez dira erabili zenbait hilabetez"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"Baimenak kendu zaizkien aplikazioak"</string> <string name="permission_removed_page_title" msgid="2627436155091001209">"Baimenak kendu zaizkien aplikazioak"</string> @@ -354,11 +355,11 @@ <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Aplikazio horiek pantaila, egiten dituzun ekintzak eta idazten dituzun gauzak ikusi ahalko dituzte, bai eta ekintzak gauzatu eta pantaila kontrolatu ere."</string> <string name="role_assistant_label" msgid="4727586018198208128">"Laguntzaile digitalaren aplikazio lehenetsia"</string> <string name="role_assistant_short_label" msgid="3369003713187703399">"Laguntzaile digitalaren aplikazioa"</string> - <string name="role_assistant_description" msgid="6622458130459922952">"Ikusten ari zaren pantailako informazioaren araberako laguntza eskain diezazukete laguntza-aplikazioek. Zenbait aplikaziok abiarazlea eta ahots bidezko zerbitzuak onartzen dituzte laguntza integratua eskaintzeko."</string> + <string name="role_assistant_description" msgid="6622458130459922952">"Ikusten ari zaren pantailako informazioaren araberako laguntza eskain diezazukete laguntza-aplikazioek. Zenbait aplikaziok exekutatzeko tresna eta ahots bidezko zerbitzuak onartzen dituzte laguntza integratua eskaintzeko."</string> <string name="role_browser_label" msgid="2877796144554070207">"Arakatzaile lehenetsia"</string> - <string name="role_browser_short_label" msgid="6745009127123292296">"Arakatzaile-aplikazioa"</string> + <string name="role_browser_short_label" msgid="6745009127123292296">"Arakatzaile-apliU+2060kazioa"</string> <string name="role_browser_description" msgid="3465253637499842671">"Interneteko sarbidea ematen dizuten eta sakatzen dituzun estekak bistaratzen dituzten aplikazioak"</string> - <string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu arakatzaile-aplikazio lehenetsi gisa?"</string> + <string name="role_browser_request_title" msgid="2895200507835937192">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu arakatzaile lehenetsi gisa?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"Ez du behar baimenik"</string> <string name="role_dialer_label" msgid="1100224146343237968">"Telefono lehenetsia"</string> <string name="role_dialer_short_label" msgid="7186888549465352489">"Telefonoa aplikazioa"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string> <string name="role_home_label" msgid="3871847846649769412">"Hasierako aplikazio lehenetsia"</string> <string name="role_home_short_label" msgid="8544733747952272337">"Hasierako aplikazioa"</string> - <string name="role_home_description" msgid="7997371519626556675">"Android-eko gailuko orri nagusiak ordezten dituzten aplikazioak (\"abiarazle\" ere deitzen zaie). Gailuko eduki eta eginbideetarako sarbidea ematen dute."</string> + <string name="role_home_description" msgid="7997371519626556675">"Android-eko gailuko orri nagusiak ordezten dituzten aplikazioak (\"exekutatzeko tresna\" ere deitzen zaie). Gailuko eduki eta eginbideetarako sarbidea ematen dute."</string> <string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezarri nahi duzu hasierako aplikazio lehenetsi gisa?"</string> <string name="role_home_request_description" msgid="2658833966716057673">"Ez du behar baimenik"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"abiarazlea"</string> @@ -422,7 +423,7 @@ <string name="system_uses_camera" msgid="1911223105234441470">"Sistemaren zerbitzu bat kamera erabiltzen ari da"</string> <string name="other_use" msgid="6564855051022776692">"Besta erabilera bat:"</string> <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"Ados"</string> - <string name="ongoing_usage_dialog_title" msgid="683836493556628569">"<xliff:g id="TYPES_LIST">%s</xliff:g> erabilitako azken aplikazioak"</string> + <string name="ongoing_usage_dialog_title" msgid="683836493556628569">"Azkenaldian <xliff:g id="TYPES_LIST">%s</xliff:g> erabilitako aplikazioak"</string> <string name="ongoing_usage_dialog_title_mic" msgid="5966714811125593992">"Mikrofonoa duela gutxi erabili dutenak"</string> <string name="ongoing_usage_dialog_title_camera" msgid="7819329688650711470">"Kamera duela gutxi erabili dutenak"</string> <string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Mikrofonoa eta kamera duela gutxi erabili dituztenak"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Bat ere ez"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(sistemaren aplikazio lehenetsia)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ez dago aplikaziorik"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"NFC bidezko beste zerbitzu batzuk"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Hautatuta"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Hautatuta - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"aplikazio-baimen bereziak"</string> @@ -621,7 +623,7 @@ <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Berrikusi kokapena atzeko planoan erabiltzeko baimena duen aplikazioa"</string> <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Aplikazioak beti erabil dezake kokapena, nahiz eta itxita egon.\n\nSegurtasun- eta larrialdi-aplikazio batzuek atzeko planoan kokapena erabiltzeko baimena behar dute behar bezala funtzionatzeko."</string> <string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Aldatu da sarbidea"</string> - <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Ikusi kokapenaren azken erabilera"</string> + <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Ikusi kokapenaren azkenaldiko erabilera"</string> <string name="privacy_controls_title" msgid="7605929972256835199">"Pribatutasun-ezarpenak"</string> <string name="camera_toggle_title" msgid="1251201397431837666">"Kamera erabiltzeko baimena"</string> <string name="mic_toggle_title" msgid="2649991093496110162">"Mikrofonoa erabiltzeko baimena"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Atzo atzitu zen (<xliff:g id="TIME_DATE">%1$s</xliff:g>)"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Atzitze-data: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> (<xliff:g id="TIME_DATE_1">%2$s</xliff:g>)"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Zure erabilera bakarreko pasahitza 132435 da"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Eman ezarpen mugatuak erabiltzeko baimena"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Murriztapenak ditu ezarpenak"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zure segurtasuna bermatzeko, ezarpena ez dago erabilgarri une honetan."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ekintza hori ez dago erabilgarri telefono-deietan"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefono-dei batean zaudenean ezin diezu eman beste aplikazio batzuk instalatzeko baimena aplikazioei.\n\n Iruzurgileek halako ekintzak egiteko eskatu ohi dute telefono bidezko elkarrizketetan; beraz, aukera hori blokeatu dugu zu babesteko. Ezagutzen ez duzun norbaitek halako zerbait egiteko esaten badizu, baliteke azpikeria izatea."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikazioari <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> erabiltzeko baimena ukatu zaio"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Kontuzko informazioa erabiltzeko baimen bat eskatu du aplikazioak, eta agian horrek arriskuan jarriko ditu zure informazio pertsonala eta finantzei buruzko informazioa.<xliff:g id="ID_1"><br><br></xliff:g>Baliteke aplikazioak behar bezala ez funtzionatzea baimen murriztu hori gabe. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Lortu baimena emateko argibideak</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikazioari <xliff:g id="ROLE_NAME">%1$s</xliff:g> lehenetsia izateko baimena ukatu zaio"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ados"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Blokeatu da baimen-eskaera"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Baimen gehigarriak eskatzen ari da aplikazioa, baina ezin da eman baimenik igorpen-saioetan. Lehenik eta behin, eman baimena telefonoan."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Larrialdi-deiak egitean edo larrialdietarako testu-mezuak bidaltzean"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Kokapena larrialdi-zerbitzuei bidali zaie"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aplikazioak gailuaren kokapena atzitu du larrialdietarako zenbaki batera dei bat egin edo testu-mezu bat bidali duzun bitartean. Baliteke aplikazioak kokapena erabiltzeko baimena ez izan edo gailuaren kokapena desaktibatuta egon arren gertatzea hori. "<a href="https://support.google.com/android/answer/9319337">"Lortu informazio gehiago"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Begiratu telefonoan"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Ezin izan da konektatu telefonora"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Berriro saiatu nahi duzu?"</string> </resources> diff --git a/PermissionController/res/values-fa-v35/strings.xml b/PermissionController/res/values-fa-v35/strings.xml new file mode 100644 index 000000000..5bc65a09d --- /dev/null +++ b/PermissionController/res/values-fa-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"امنیت"</string> +</resources> diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml index d5e5af4ad..f04d8f4c9 100644 --- a/PermissionController/res/values-fa/strings.xml +++ b/PermissionController/res/values-fa/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"اجازهها"</string> <string name="cancel" msgid="8943320028373963831">"لغو"</string> <string name="back" msgid="6249950659061523680">"برگشت"</string> + <string name="dialog_close" msgid="6840699812532384661">"بستن"</string> <string name="available" msgid="6007778121920339498">"دردسترس"</string> <string name="blocked" msgid="9195547604866033708">"مسدود"</string> <string name="on" msgid="280241003226755921">"روشن"</string> @@ -249,13 +250,13 @@ <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"درحالحاضر رد شده است / آخرین دسترسی: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"هرگز دسترسی نداشته است"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"غیرمجاز / هرگز دسترسی نداشته است"</string> - <string name="allowed_header" msgid="7769277978004790414">"مجاز است"</string> + <string name="allowed_header" msgid="7769277978004790414">"مجاز بودن"</string> <string name="allowed_always_header" msgid="6455903312589013545">"همیشه مجاز بودن"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"مجاز فقط هنگام استفاده"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"مجاز فقط برای دسترسی به رسانهها"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"مجاز برای مدیریت همه فایلها"</string> <string name="ask_header" msgid="2633816846459944376">"هربار پرسیده شود"</string> - <string name="denied_header" msgid="903209608358177654">"مجاز نبودن"</string> + <string name="denied_header" msgid="903209608358177654">"مجاز نیست"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> در <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"دیدن برنامههای دیگری که به همه فایلها دسترسی دارند"</string> <string name="days" msgid="609563020985571393">"{count,plural, =1{یک روز}one{# روز}other{# روز}}"</string> @@ -265,11 +266,11 @@ <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> - <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"برای محافظت از حریم خصوصی شما، اجازهها برداشته شد. برای مرور، ضربه بزنید"</string> + <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"برای محافظت از حریم خصوصی شما، اجازهها برداشته شد. برای مرور، تکضرب بزنید"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"اجازههای مربوط به برنامههای استفادهنشده برداشته شد"</string> - <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"برخی از برنامهها بهمدت چند ماه استفاده نشدهاند. برای مرور، ضربه بزنید."</string> + <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"برخی از برنامهها بهمدت چند ماه استفاده نشدهاند. برای مرور، تکضرب بزنید."</string> <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# برنامه استفادهنشده}one{# برنامه استفادهنشده}other{# برنامه استفادهنشده}}"</string> - <string name="unused_apps_notification_content" msgid="9195026773244581246">"اجازهها و فایلهای موقت حذف شدهاند و اعلانها متوقف شدهاند. برای مرور، ضربه بزنید."</string> + <string name="unused_apps_notification_content" msgid="9195026773244581246">"اجازهها و فایلهای موقت حذف شدهاند و اعلانها متوقف شدهاند. برای مرور، تکضرب بزنید."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"مرور برنامههایی که اجازههای آنها برداشته شده است"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"اجازهها و فایلهای موقت مربوط به برنامههایی که مدتی از آنها استفاده نکردهاید برداشته شدند و اعلانها متوقف شدند."</string> <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"مرور برنامهها"</string> @@ -286,7 +287,7 @@ <string name="months_ago" msgid="1766026492610646354">"<xliff:g id="COUNT">%1$d</xliff:g> ماه قبل"</string> <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"برای محافظت از حریم خصوصی شما، اجازهها برداشته شد"</string> <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> به موقعیت مکانی شما در پسزمینه دسترسی دارد"</string> - <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"این برنامه همیشه میتواند به مکانتان دسترسی داشته باشد. برای تغییر دادن، ضربه بزنید."</string> + <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"این برنامه همیشه میتواند به مکانتان دسترسی داشته باشد. برای تغییر دادن، تکضرب بزنید."</string> <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"مرور برنامهٔ دارای دسترسی به اعلانها"</string> <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> میتواند اعلانها را رد کند، روی آنها کنش انجام دهد، و به محتوای داخل آنها دسترسی داشته باشد"</string> <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"این برنامه میتواند اعلانها را رد کند، روی آنها کنش انجام دهد، و به محتوای داخل آنها دسترسی داشته باشد. برخی برنامهها به این نوع دسترسی نیاز دارند تا طبق برنامه کار کنند."</string> @@ -301,12 +302,12 @@ <string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"دسترسی برداشته شد"</string> <string name="safety_center_notification_app_label" msgid="2457720616141926534">"سیستم Android"</string> <string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"برای محافظت از حریم خصوصی، اجازههای برنامه برداشته شدند"</string> - <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> طی چند ماه استفاده نشده است. برای مرور، ضربه بزنید."</string> - <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> و ۱ برنامه دیگر طی چند ماه استفاده نشدهاند. برای مرور، ضربه بزنید."</string> - <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> و <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> برنامه دیگر طی چند ماه استفاده نشدهاند. برای مرور، ضربه بزنید."</string> + <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> طی چند ماه استفاده نشده است. برای مرور، تکضرب بزنید."</string> + <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> و ۱ برنامه دیگر طی چند ماه استفاده نشدهاند. برای مرور، تکضرب بزنید."</string> + <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> و <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> برنامه دیگر طی چند ماه استفاده نشدهاند. برای مرور، تکضرب بزنید."</string> <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"۱ برنامه استفاده نشده است"</string> <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> برنامه استفاده نشدهاند"</string> - <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"برای محافظت از حریم خصوصی، اجازهها برداشته خواهند شد. برای مرور، ضربه بزنید."</string> + <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"برای محافظت از حریم خصوصی، اجازهها برداشته خواهند شد. برای مرور، تکضرب بزنید."</string> <string name="unused_apps_title" msgid="8589298917717872239">"برنامههای استفادهنشده"</string> <string name="unused_apps_subtitle_after" msgid="2034267519506357898">"اجازهها از این برنامهها برداشته شد"</string> <string name="unused_apps_subtitle_before" msgid="5233302577076132427">"اجازهها از این برنامهها برداشته خواهند شد"</string> @@ -319,7 +320,7 @@ <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"تنها هنگام استفاده از برنامه"</string> <string name="permission_subtitle_media_only" msgid="8917869683764720717">"رسانه"</string> <string name="permission_subtitle_all_files" msgid="4982613338298067862">"همه فایلها"</string> - <string name="permission_subtitle_background" msgid="8916750995309083180">"همیشه مجاز بودن"</string> + <string name="permission_subtitle_background" msgid="8916750995309083180">"همیشه مجاز است"</string> <string name="app_perms_24h_access" msgid="99069906850627181">"آخرین زمان دسترسی: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_perms_24h_access_yest" msgid="5411926024794555022">"آخرین زمان دسترسی دیروز ساعت <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_perms_7d_access" msgid="4945055548894683751">"آخرین دسترسی: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ساعت <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> @@ -345,7 +346,7 @@ <string name="no_apps_allowed" msgid="7718822655254468631">"برنامهای مجاز نشده"</string> <string name="no_apps_allowed_full" msgid="8011716991498934104">"برنامهای برای همه فایلها مجاز نشده"</string> <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"برنامهای برای فقط رسانه مجاز نشده"</string> - <string name="no_apps_denied" msgid="7663435886986784743">"برنامهای غیرمجاز نشده"</string> + <string name="no_apps_denied" msgid="7663435886986784743">"برنامهای رد نشده"</string> <string name="car_permission_selected" msgid="180837028920791596">"انتخابشده"</string> <string name="settings" msgid="5409109923158713323">"تنظیمات"</string> <string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> سرویس به دستگاه شما دسترسی کامل دارند"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"ice"</string> <string name="role_home_label" msgid="3871847846649769412">"برنامه صفحه اصلی پیشفرض"</string> <string name="role_home_short_label" msgid="8544733747952272337">"برنامه صفحه اصلی"</string> - <string name="role_home_description" msgid="7997371519626556675">"برنامههای عموماً موسوم به راهانداز، که جایگزین صفحههای اصلی در دستگاه Android میشوند و امکان دسترسی به محتواها و ویژگیهای دستگاه را میدهند"</string> + <string name="role_home_description" msgid="7997371519626556675">"برنامههایی (موسوم به راهانداز) که جایگزین صفحههای اصلی در دستگاه Android میشوند و امکان دسترسی به محتواها و ویژگیهای دستگاه را میدهند"</string> <string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> بهعنوان برنامه صفحه اصلی پیشفرض تنظیم شود؟"</string> <string name="role_home_request_description" msgid="2658833966716057673">"اجازه لازم نیست"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"راهانداز"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"هیچکدام"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(پیشفرض سیستم)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"برنامهای موجود نیست"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"دیگر سرویسهای NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"انتخاب شد"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"انتخابشده - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"دسترسی ویژه برنامه"</string> @@ -469,7 +471,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه میدهید به عکسها و رسانههای «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه دسترسی به مخاطبینتان میدهید؟"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه میدهید به مخاطبین شما در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه دسترسی به مکان این دستگاه میدهید؟"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه دسترسی به مکان این دستگاه را میدهید؟"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه میدهید به مکان «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"این برنامه فقط وقتی از آن استفاده میکنید، به مکان دسترسی خواهد داشت"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟"</string> @@ -483,7 +485,7 @@ <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود دستگاههای اطراف را پیدا کند، به آنها متصل شود، و موقعیت نسبی آنها را مشخص کند؟ "<annotation id="link">"در تنظیمات اجازه دهید."</annotation></string> <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"دسترسی مکان <xliff:g id="APP_NAME"><b>%1$s</b></xliff:g> را از حالت تقریبی به دقیق تغییر میدهید؟"</string> <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"دسترسی <xliff:g id="APP_NAME"><b>%1$s</b></xliff:g> به مکان «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» از تقریبی به دقیق تغییر کند؟"</string> - <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>؛ اجازه دسترسی به مکان تقریبی این دستگاه را میدهید؟"</string> + <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه دسترسی به مکان تقریبی این دستگاه را میدهید؟"</string> <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه میدهید به مکان تقریبی «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</string> <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"دقیق"</string> <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"تقریبی"</string> @@ -579,7 +581,7 @@ <string name="sensor_permissions_qs" msgid="1022267900031317472">"اجازهها"</string> <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"امنیت و حریم خصوصی"</string> <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"بررسی وضعیت"</string> - <string name="privacy_controls_qs" msgid="5780144882040591169">"تنظیمات حریم خصوصی شما"</string> + <string name="privacy_controls_qs" msgid="5780144882040591169">"کنترلهای حریم خصوصی شما"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"تنظیمات بیشتر"</string> <string name="camera_toggle_label_qs" msgid="3880261453066157285">"دسترسی به دوربین"</string> <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"دسترسی به میکروفون"</string> @@ -599,7 +601,7 @@ <string name="active_app_usage_2_qs" msgid="6107866785243565283">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال استفاده از آن است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string> <string name="recent_app_usage_2_qs" msgid="3591205954235694403">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string> <string name="media_confirm_dialog_positive_button" msgid="9020793594051526399">"تأیید"</string> - <string name="media_confirm_dialog_negative_button" msgid="226987376924861785">"برگشت"</string> + <string name="media_confirm_dialog_negative_button" msgid="226987376924861785">"برگشتن"</string> <string name="media_confirm_dialog_title_a_to_p_aural_allow" msgid="8560601114044699903">"دسترسی به فایلهای دیگر نیز مجاز میشود"</string> <string name="media_confirm_dialog_title_a_to_p_aural_deny" msgid="7841428716317307685">"دسترسی به فایلهای دیگر نیز مجاز نمیشود"</string> <string name="media_confirm_dialog_title_a_to_p_visual_allow" msgid="6469086448310893751">"دسترسی به فایلهای دیگر نیز مجاز میشود"</string> @@ -622,7 +624,7 @@ <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"این برنامه همیشه میتواند به مکانتان دسترسی داشته باشد، حتی وقتی بسته باشد.\n\nبرخیاز برنامههای ایمنی و اضطراری برای اینکه عملکرد موردانتظار را داشته باشند باید به مکان شما در پسزمینه دسترسی داشته باشند."</string> <string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"دسترسی تغییر کرد"</string> <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"دیدن استفاده اخیر از مکان"</string> - <string name="privacy_controls_title" msgid="7605929972256835199">"تنظیمات حریم خصوصی"</string> + <string name="privacy_controls_title" msgid="7605929972256835199">"کنترلهای حریم خصوصی"</string> <string name="camera_toggle_title" msgid="1251201397431837666">"دسترسی به دوربین"</string> <string name="mic_toggle_title" msgid="2649991093496110162">"دسترسی به میکروفون"</string> <string name="perm_toggle_description" msgid="7801326363741451379">"برای برنامهها و سرویسها"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"دسترسی در روز گذشته ساعت <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"دسترسی در <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ساعت <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"گذرواژه یکبارمصرف شما 132435 است"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"مجاز کردن تنظیمات محدودشده"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"تنظیم محدودشده"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"برای حفظ امنیت شما، درحالحاضر این تنظیم دردسترس نیست."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"اقدام درطول تماس تلفنی دردسترس نیست"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"درطول تماس تلفنی، اجازه دادن به برنامهها برای نصب برنامههای دیگر مجاز نیست.\n\n افراد کلاهبردار اغلب این نوع اقدامات را درطول مکالمههای تلفنی درخواست میکنند، بنابراین این کار برای محافظت از شما مسدود شده است. اگر شخص ناآشنایی شما را ترغیب به انجام این اقدام میکند، ممکن است موضوع کلاهبرداری باشد."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"درخواست برنامه برای دسترسی به <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> رد شد"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"این برنامه درخواست دسترسی به اجازهای حساس را داشته است که میتواند اطلاعات شخصی و مالیتان را درمعرض خطر قرار دهد.<xliff:g id="ID_1"><br><br></xliff:g>ممکن است برنامه بدون این اجازه محدودشده بهدرستی کار نکند. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>با نحوه اعطای دسترسی آشنا شوید</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"درخواست دسترسی برنامه برای تبدیل شدن به <xliff:g id="ROLE_NAME">%1$s</xliff:g> پیشفرض رد شد"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"بسیارخوب"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"درخواست اجازه رد شد"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"این برنامه درخواست اجازههای اضافی دارد، اما امکان دادن این اجازهها در جلسه جاریسازی وجود ندارد. ابتدا اجازه را در تلفنتان اعطا کنید."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"برای تماس یا پیامک اضطراری"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"مکان به خدمات اضطراری ارسال شد"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"این برنامه درحین تماس با شماره تلفن اضطراری یا ارسال پیامک به آن به مکان دستگاهتان دسترسی پیدا کرده است. حتی زمانیکه برنامه اجازه مکان ندارد یا مکان دستگاه خاموش است، ممکن است این اتفاق رخ دهد. "<a href="https://support.google.com/android/answer/9319337">"بیشتر بدانید"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"بررسی تلفن"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ارتباط با تلفن برقرار نشد"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"مجدداً امتحان میکنید؟"</string> </resources> diff --git a/PermissionController/res/values-fi-v35/strings.xml b/PermissionController/res/values-fi-v35/strings.xml new file mode 100644 index 000000000..a1c8d62d4 --- /dev/null +++ b/PermissionController/res/values-fi-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Suojaus"</string> +</resources> diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml index 6e687f613..a88c39ab2 100644 --- a/PermissionController/res/values-fi/strings.xml +++ b/PermissionController/res/values-fi/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"käyttöoikeudet"</string> <string name="cancel" msgid="8943320028373963831">"Peru"</string> <string name="back" msgid="6249950659061523680">"Takaisin"</string> + <string name="dialog_close" msgid="6840699812532384661">"Sulje"</string> <string name="available" msgid="6007778121920339498">"Käytettävissä"</string> <string name="blocked" msgid="9195547604866033708">"Estetty"</string> <string name="on" msgid="280241003226755921">"Päällä"</string> @@ -195,7 +196,7 @@ <string name="precise_image_description" msgid="6349638632303619872">"Tarkka sijainti"</string> <string name="approximate_image_description" msgid="938803699637069884">"Likimääräinen sijainti"</string> <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Käytä tarkkaa sijaintia"</string> - <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kun tarkka sijainti ei ole päällä, sovellukset voivat nähdä likimääräisen sijaintisi"</string> + <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kun tarkka sijainti ei ole päällä, sovellukset voivat käyttää likimääräistä sijaintiasi"</string> <string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>lupa"</string> <string name="app_permission_header" msgid="2951363137032603806">"Sovellus pyytää <xliff:g id="PERM">%1$s</xliff:g>-lupaa"</string> <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Tällä sovelluksella on pääsy (<xliff:g id="PERM">%1$s</xliff:g>) laitteella <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ei mitään"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Järjestelmän oletusarvo)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ei sovelluksia"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Muut NFC-palvelut"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valittu"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valittu – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"sovellusten erikoiskäyttö"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Avattu eilen klo <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Avattu <xliff:g id="TIME_DATE_0">%1$s</xliff:g> klo <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Kertakäyttöinen salasanasi on 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Salli rajoitetut asetukset"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Rajoitettu asetus"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Asetus ei ole tällä hetkellä käytettävissä turvallisuussyistä."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Toiminto ei ole käytettävissä puhelun aikana"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Sovellusten ei sallita asentaa muita sovelluksia puhelun aikana.\n\n Huijarit pyytävät usein tätä puhelun aikana, joten se on estetty suojataksesi sinua. Jos joku, jota et tunne, kehottaa sinua toimimaan näin, kyseessä voi olla huijaus."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Sovellukselta on evätty pääsy: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Sovellus on pyytänyt pääsyä arkaluontoiseen lupaan, joka voi vaarantaa henkilökohtaisia tietojasi ja taloustietojasi.<xliff:g id="ID_1"><br><br></xliff:g>Sovellus ei välttämättä toimi oikein ilman tätä rajoitettua lupaa. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Lue, miten voit sallia pääsyn</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Sovellus ei saa olla oletuksena <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Lupapyyntö estetty"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Sovellus pyytää lisälupia, mutta lupia ei voi myöntää striimatessa. Myönnä lupa ensin puhelimella."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Soitto tai tekstiviesti hätäkeskukselle"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Sijainti lähetetty hätäkeskukselle"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Tämä sovellus sai pääsyn laitteen sijaintiin, kun soitit tai lähetit tekstiviestin hätänumeroon. Tämä voi tapahtua, vaikka sovelluksella ei olisi sijaintilupaa tai laitteen sijainti olisi pois päältä. "<a href="https://support.google.com/android/answer/9319337">"Lue lisää"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Tarkista puhelimesi"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Puhelimeesi ei saatu yhteyttä"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Yritetäänkö uudelleen?"</string> </resources> diff --git a/PermissionController/res/values-fr-rCA-car/strings.xml b/PermissionController/res/values-fr-rCA-car/strings.xml index 02b0a45be..5be1f89db 100644 --- a/PermissionController/res/values-fr-rCA-car/strings.xml +++ b/PermissionController/res/values-fr-rCA-car/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="unused_apps_page_summary" msgid="7505839764289846511">"Si une application n\'est pas utilisée pendant quelques mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage."</string> + <string name="unused_apps_page_summary" msgid="7505839764289846511">"Si une appli n\'est pas utilisée pendant quelques mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage."</string> </resources> diff --git a/PermissionController/res/values-fr-rCA-television/strings.xml b/PermissionController/res/values-fr-rCA-television/strings.xml index 84836a119..f4ec46457 100644 --- a/PermissionController/res/values-fr-rCA-television/strings.xml +++ b/PermissionController/res/values-fr-rCA-television/strings.xml @@ -17,11 +17,11 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="grant_dialog_button_deny_dont_ask_again" msgid="747769682501286250">"Refuser et ne plus demander"</string> - <string name="grant_dialog_how_to_change" msgid="997462845048160559">"Vous pourrez modifier ce choix plus tard dans le menu Paramètres > Applications"</string> + <string name="grant_dialog_how_to_change" msgid="997462845048160559">"Vous pourrez modifier ce choix plus tard dans le menu Paramètres > Applis"</string> <string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> - <string name="preference_show_system_apps" msgid="4262140518693221093">"Afficher les applications système"</string> - <string name="app_permissions_decor_title" msgid="7438716722786036814">"Autorisations des applications"</string> - <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations des applications"</string> + <string name="preference_show_system_apps" msgid="4262140518693221093">"Afficher les applis système"</string> + <string name="app_permissions_decor_title" msgid="7438716722786036814">"Autorisations des applis"</string> + <string name="manage_permissions_decor_title" msgid="4138423885439613577">"Autorisations des applis"</string> <string name="permission_apps_decor_title" msgid="2811550489429789828">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string> <string name="additional_permissions_decor_title" msgid="5113847982502484225">"Autorisations supplémentaires"</string> <string name="system_apps_decor_title" msgid="4402004958937474803">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string> diff --git a/PermissionController/res/values-fr-rCA-v33/strings.xml b/PermissionController/res/values-fr-rCA-v33/strings.xml index 808e1bcd9..a2f81bcea 100644 --- a/PermissionController/res/values-fr-rCA-v33/strings.xml +++ b/PermissionController/res/values-fr-rCA-v33/strings.xml @@ -16,9 +16,9 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="role_dialer_request_description" msgid="6188305064871543419">"Cette application sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string> - <string name="role_sms_request_description" msgid="1506966389698625395">"Cette application sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à vos fichiers, à votre microphone, à votre téléphone et à vos messages texte"</string> - <string name="permission_description_summary_storage" msgid="1917071243213043858">"Les applications possédant cette autorisation peuvent accéder à tous les fichiers sur cet appareil"</string> + <string name="role_dialer_request_description" msgid="6188305064871543419">"Cette appli sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string> + <string name="role_sms_request_description" msgid="1506966389698625395">"Cette appli sera autorisée à vous envoyer des notifications; elle aura accès à votre appareil photo, à vos contacts, à vos fichiers, à votre microphone, à votre téléphone et à vos messages texte"</string> + <string name="permission_description_summary_storage" msgid="1917071243213043858">"Les applis possédant cette autorisation peuvent accéder à tous les fichiers sur cet appareil"</string> <string name="work_policy_title" msgid="832967780713677409">"Infos sur votre politique de travail"</string> <string name="work_policy_summary" msgid="3886113358084963931">"Les paramètres sont gérés par votre administrateur informatique"</string> <string name="safety_center_entry_group_expand_action" msgid="5358289574941779652">"Développer et afficher la liste"</string> diff --git a/PermissionController/res/values-fr-rCA-v34/strings.xml b/PermissionController/res/values-fr-rCA-v34/strings.xml index 4b18ba2f6..227e9513a 100644 --- a/PermissionController/res/values-fr-rCA-v34/strings.xml +++ b/PermissionController/res/values-fr-rCA-v34/strings.xml @@ -20,7 +20,7 @@ <string name="security_privacy_brand_name" msgid="7303621734258440812">"Sécurité et confidentialité"</string> <string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Commandes"</string> <string name="health_connect_title" msgid="2132233890867430855">"Connexion santé"</string> - <string name="health_connect_summary" msgid="815473513776882296">"Gérez l\'accès des applications aux données relatives à la santé"</string> + <string name="health_connect_summary" msgid="815473513776882296">"Gérez l\'accès des applis aux données relatives à la santé"</string> <string name="location_settings" msgid="8863940440881290182">"Accès à la position"</string> - <string name="mic_toggle_description" msgid="1504101620086616040">"Pour les applications et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence"</string> + <string name="mic_toggle_description" msgid="1504101620086616040">"Pour les applis et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence"</string> </resources> diff --git a/PermissionController/res/values-fr-rCA-v35/strings.xml b/PermissionController/res/values-fr-rCA-v35/strings.xml new file mode 100644 index 000000000..9a88205ed --- /dev/null +++ b/PermissionController/res/values-fr-rCA-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sécurité"</string> +</resources> diff --git a/PermissionController/res/values-fr-rCA-watch/strings.xml b/PermissionController/res/values-fr-rCA-watch/strings.xml index 4d588d0c9..aa512a724 100644 --- a/PermissionController/res/values-fr-rCA-watch/strings.xml +++ b/PermissionController/res/values-fr-rCA-watch/strings.xml @@ -18,7 +18,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="grant_dialog_button_deny_dont_ask_again" msgid="5709879604352260492">"Refuser et ne plus demander"</string> <string name="current_permission_template" msgid="6634462553790549887">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> - <string name="preference_show_system_apps" msgid="1055740303992024300">"Afficher les applications système"</string> + <string name="preference_show_system_apps" msgid="1055740303992024300">"Afficher les applis système"</string> <string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Inchangeable"</string> <string name="generic_yes" msgid="2489207724988649846">"Oui"</string> <string name="generic_cancel" msgid="2631708607129269698">"Annuler"</string> diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml index d84795589..c7941a592 100644 --- a/PermissionController/res/values-fr-rCA/strings.xml +++ b/PermissionController/res/values-fr-rCA/strings.xml @@ -21,12 +21,13 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"autorisations"</string> <string name="cancel" msgid="8943320028373963831">"Annuler"</string> <string name="back" msgid="6249950659061523680">"Retour"</string> + <string name="dialog_close" msgid="6840699812532384661">"Fermer"</string> <string name="available" msgid="6007778121920339498">"Accessible"</string> <string name="blocked" msgid="9195547604866033708">"Bloqué"</string> <string name="on" msgid="280241003226755921">"Activé"</string> <string name="off" msgid="1438489226422866263">"Désactivé"</string> <string name="uninstall_or_disable" msgid="4496612999740858933">"Désinstaller ou désactiver"</string> - <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Application non trouvée"</string> + <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Appli non trouvée"</string> <string name="grant_dialog_button_deny" msgid="88262611492697192">"Ne pas autoriser"</string> <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Ne pas autoriser et ne plus demander"</string> <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder « Pendant l\'utilisation de l\'appli »"</string> @@ -43,7 +44,7 @@ <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> <string name="permission_warning_template" msgid="2247087781222679458">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à <xliff:g id="ACTION">%2$s</xliff:g>?"</string> <string name="permission_add_background_warning_template" msgid="1812914855915092273">"Toujours autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à <xliff:g id="ACTION">%2$s</xliff:g>?"</string> - <string name="allow_permission_foreground_only" msgid="116465816039675404">"Seulement durant l\'utilisation de l\'application"</string> + <string name="allow_permission_foreground_only" msgid="116465816039675404">"Seulement durant l\'utilisation de l\'appli"</string> <string name="allow_permission_always" msgid="5194342531206054051">"Toujours"</string> <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"Ne pas autoriser et ne plus demander"</string> <string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g> autorisation(s) désactivée(s)"</string> @@ -58,12 +59,12 @@ <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"Toujours autoriser"</string> <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Autoriser à gérer tous les fichiers"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Autoriser l\'accès aux fichiers multimédias"</string> - <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applications"</string> - <string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string> - <string name="unused_apps" msgid="2058057455175955094">"Applications non utilisées"</string> - <string name="edit_photos_description" msgid="5540108003480078892">"Modifiez une sélection de photos pour cette application"</string> - <string name="no_unused_apps" msgid="12809387670415295">"Aucune application inutilisée"</string> - <string name="zero_unused_apps" msgid="9024448554157499748">"Aucune application inutilisée"</string> + <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applis"</string> + <string name="app_permissions" msgid="3369917736607944781">"Autorisations des applis"</string> + <string name="unused_apps" msgid="2058057455175955094">"Applis non utilisées"</string> + <string name="edit_photos_description" msgid="5540108003480078892">"Modifiez une sélection de photos pour cette appli"</string> + <string name="no_unused_apps" msgid="12809387670415295">"Aucune appli inutilisée"</string> + <string name="zero_unused_apps" msgid="9024448554157499748">"Aucune appli inutilisée"</string> <string name="review_permission_decisions" msgid="309559429150613632">"Décisions d\'autorisation récentes"</string> <string name="review_permission_decisions_view_all" msgid="90391040431566130">"Afficher toutes les décisions d\'autorisation récentes"</string> <string name="review_permission_decisions_empty" msgid="8120775336417279806">"Aucune décision d\'autorisation récente"</string> @@ -71,37 +72,37 @@ <string name="granted_permission_decision" msgid="7824827491551861365">"Vous avez autorisé <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> <string name="denied_permission_decision" msgid="5308961501779563781">"Vous avez refusé l\'accès de <xliff:g id="APP_NAME">%1$s</xliff:g> aux <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Aujourd’hui}=1{Il y a 1 jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string> - <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'application"</string> - <string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette application, Android et d\'autres applications risquent de ne plus fonctionner correctement. Gardez à l\'esprit que vous ne pouvez pas supprimer cette application, étant donné qu\'elle était préinstallée sur votre appareil. En la désactivant, elle ne sera plus active et elle sera masquée sur votre appareil."</string> + <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'appli"</string> + <string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette appli, Android et d\'autres applis risquent de ne plus fonctionner correctement. Gardez à l\'esprit que vous ne pouvez pas supprimer cette appli, étant donné qu\'elle était préinstallée sur votre appareil. En la désactivant, elle ne sera plus active et elle sera masquée sur votre appareil."</string> <string name="app_permission_manager" msgid="3903811137630909550">"Gestionnaire des autorisations"</string> <string name="never_ask_again" msgid="4728762438198560329">"Ne plus demander"</string> <string name="no_permissions" msgid="3881676756371148563">"Aucune autorisation"</string> <string name="additional_permissions" msgid="5801285469338873430">"Autorisations supplémentaires"</string> - <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ouvrir l\'information sur l\'application"</string> + <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Ouvrir l\'information sur l\'appli"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# autre}one{# autre}many{# autres}other{# autres}}"</string> - <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Cette application a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."</string> - <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Cette application a été conçue pour une ancienne version d\'Android. Si vous acceptez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera autorisé."</string> - <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera refusé."</string> + <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Cette appli a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."</string> + <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous acceptez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera autorisé."</string> + <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez cette autorisation, l\'accès à tout l\'espace de stockage (y compris les photos, les vidéos, les fichiers musicaux et audio ainsi que d\'autres fichiers) sera refusé."</string> <string name="default_permission_description" msgid="4624464917726285203">"effectuer une action inconnue"</string> - <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string> - <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string> + <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> appli(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string> + <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g> appli(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string> <string name="menu_show_system" msgid="4254021607027872504">"Afficher le système"</string> <string name="menu_hide_system" msgid="3855390843744028465">"Masquer le système"</string> <string name="menu_show_7_days_data" msgid="8979611198508523706">"Afficher les 7 derniers jours"</string> <string name="menu_show_24_hours_data" msgid="8228054833323380780">"Afficher les dernières 24 heures"</string> <string name="manage_permission" msgid="2895385393037061964">"Gérer les autorisations"</string> - <string name="no_apps" msgid="2412612731628386816">"Aucune application"</string> + <string name="no_apps" msgid="2412612731628386816">"Aucune appli"</string> <string name="location_settings" msgid="3624412509133422562">"Paramètres de localisation"</string> <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> est un fournisseur de services de localisation pour cet appareil. L\'accès à la position peut être modifié dans le menu des paramètres de localisation."</string> <string name="system_warning" msgid="1173400963234358816">"Si vous refusez cette autorisation, il est possible que cela touche certaines fonctionnalités de base de votre appareil."</string> - <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez à cette application l\'accès aux photos et aux vidéos, l\'accès aux fichiers musicaux et audio sera également refusé."</string> - <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Cette application a été conçue pour une ancienne version d\'Android. Si vous refusez à cette application l\'accès aux fichiers musicaux et audio, l\'accès aux photos et aux vidéos sera également refusé."</string> - <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Si vous refusez cette autorisation, certaines fonctionnalités de votre appareil gérées par cette application risquent de ne plus fonctionner correctement."</string> + <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez à cette appli l\'accès aux photos et aux vidéos, l\'accès aux fichiers musicaux et audio sera également refusé."</string> + <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Cette appli a été conçue pour une ancienne version d\'Android. Si vous refusez à cette appli l\'accès aux fichiers musicaux et audio, l\'accès aux photos et aux vidéos sera également refusé."</string> + <string name="cdm_profile_revoke_warning" msgid="4443893270719106700">"Si vous refusez cette autorisation, certaines fonctionnalités de votre appareil gérées par cette appli risquent de ne plus fonctionner correctement."</string> <string name="permission_summary_enforced_by_policy" msgid="4443598170942950519">"Activé conformément à la politique"</string> <string name="permission_summary_disabled_by_policy_background_only" msgid="221995005556362660">"L\'accès en arrière-plan est désactivé par la politique"</string> <string name="permission_summary_enabled_by_policy_background_only" msgid="8287675974767104279">"L\'accès en arrière-plan est activé par la politique"</string> <string name="permission_summary_enabled_by_policy_foreground_only" msgid="3844582916889767831">"L\'accès en avant-plan est activé par la politique"</string> - <string name="permission_summary_enforced_by_admin" msgid="822702574117248700">"Contrôlé par l\'administrateur"</string> + <string name="permission_summary_enforced_by_admin" msgid="822702574117248700">"Contrôlée par l\'administrateur"</string> <string name="permission_summary_disabled_by_admin_background_only" msgid="3127091456731845646">"L\'accès en arrière-plan est désactivé par l\'administrateur"</string> <string name="permission_summary_enabled_by_admin_background_only" msgid="9132423838440275757">"L\'accès en arrière-plan est activé par l\'administrateur"</string> <string name="permission_summary_enabled_by_admin_foreground_only" msgid="1298432715610745358">"L\'accès en avant-plan est activé par l\'administrateur"</string> @@ -114,15 +115,15 @@ <string name="permission_access_never" msgid="4647014230217936900">"Ne pas autoriser"</string> <string name="loading" msgid="4789365003890741082">"Chargement en cours…"</string> <string name="all_permissions" msgid="6911125611996872522">"Toutes les autorisations"</string> - <string name="other_permissions" msgid="2901186127193849594">"Autres autorisations de l\'application"</string> + <string name="other_permissions" msgid="2901186127193849594">"Autres autorisations de l\'appli"</string> <string name="permission_request_title" msgid="8790310151025020126">"Demande d\'autorisation"</string> - <string name="permission_review_title_template_install" msgid="1284337937156289081">"Définissez les autorisations d\'accès de l\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string> - <string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> » a été mise à jour. Définissez ses autorisations d\'accès."</string> + <string name="permission_review_title_template_install" msgid="1284337937156289081">"Définissez les autorisations d\'accès de l\'appli « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string> + <string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'appli « <xliff:g id="APP_NAME">%1$s</xliff:g> » a été mise à jour. Définissez ses autorisations d\'accès."</string> <string name="review_button_cancel" msgid="2191147944056548886">"Annuler"</string> <string name="review_button_continue" msgid="2527918375047602199">"Continuer"</string> <string name="new_permissions_category" msgid="552995090178417611">"Nouvelles autorisations"</string> <string name="current_permissions_category" msgid="4292990083585728880">"Autorisations actuelles"</string> - <string name="message_staging" msgid="9110563899955511866">"Pré-production de l\'application en cours…"</string> + <string name="message_staging" msgid="9110563899955511866">"Pré-production de l\'appli en cours…"</string> <string name="app_name_unknown" msgid="1319665005754048952">"Inconnu"</string> <string name="permission_usage_title" msgid="1568233336351734538">"Tableau de bord de confidentialité"</string> <string name="auto_permission_usage_summary" msgid="7335667266743337075">"Voir les applis qui ont récemment utilisé des autorisations"</string> @@ -130,19 +131,19 @@ <string name="perm_usage_adv_info_title" msgid="3357831829538873708">"Voir plus d\'autorisations"</string> <string name="perm_usage_adv_info_summary_2_items" msgid="3702175198750127822">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g>"</string> <string name="perm_usage_adv_info_summary_more_items" msgid="949055326299562218">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g> et <xliff:g id="NUM">%3$s</xliff:g> autres"</string> - <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Chronologie des moments où les applications ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des dernières 24 heures"</string> - <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Chronologie des moments où les applications ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des 7 derniers jours"</string> - <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Lorsque cette application a utilisé votre autorisation de <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> + <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Chronologie des moments où les applis ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des dernières 24 heures"</string> + <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Chronologie des moments où les applis ont utilisé votre <xliff:g id="PERMGROUP">%1$s</xliff:g> au cours des 7 derniers jours"</string> + <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"Lorsque cette appli a utilisé votre autorisation de <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> <string name="permission_usage_access_dialog_learn_more" msgid="7121468469493184613">"En savoir plus"</string> <string name="learn_more_content_description" msgid="8673699744544502539">"En savoir plus sur <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> - <string name="manage_permission_summary" msgid="4117555482684114317">"Contrôlez l\'accès par les applications à votre groupe <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> + <string name="manage_permission_summary" msgid="4117555482684114317">"Contrôlez l\'accès par les applis à votre groupe <xliff:g id="PERMGROUP">%1$s</xliff:g>"</string> <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="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="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">"À tout moment"</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> @@ -163,7 +164,7 @@ <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_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_preference_label" msgid="8343167938128676378">"{count,plural, =1{Utilisation : 1 appli}one{Utilisation : # appli}many{Utilisation : # applis}other{Utilisation : # 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">"Filtré par : <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Retirer le filtre"</string> @@ -195,25 +196,25 @@ <string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string> <string name="approximate_image_description" msgid="938803699637069884">"Position approximative"</string> <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Utiliser la position exacte"</string> - <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Si la position exacte est désactivée, les applications ont accès à votre position approximative"</string> + <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Si la position exacte est désactivée, les applis ont accès à votre position approximative"</string> <string name="app_permission_title" msgid="2090897901051370711">"Autorisation : <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header" msgid="2951363137032603806">"Accès pour cette appli : <xliff:g id="PERM">%1$s</xliff:g>"</string> - <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Accès <xliff:g id="PERM">%1$s</xliff:g> pour cette application sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Accès <xliff:g id="PERM">%1$s</xliff:g> pour cette appli sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Afficher toutes les autorisations pour <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Afficher toutes les applis qui possèdent cette autorisation"</string> <string name="assistant_mic_label" msgid="1011432357152323896">"Afficher l\'usage du microphone de l\'assistant"</string> - <string name="unused_apps_category_title" msgid="2988455616845243901">"Paramètres des applications inutilisées"</string> - <string name="auto_revoke_label" msgid="5068393642936571656">"Retirer les autorisations si l\'application est inutilisée"</string> + <string name="unused_apps_category_title" msgid="2988455616845243901">"Paramètres des applis inutilisées"</string> + <string name="auto_revoke_label" msgid="5068393642936571656">"Retirer les autorisations si l\'appli est inutilisée"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Retirer autorisations et libérer espace"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"Suspendre l\'activité appli si inutilisée"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Gérer l\'appli si inutilisée"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Retirer les autorisations, supprimer les fichiers temporaires et arrêter les notifications"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Retirer les autorisations, supprimer les fichiers temporaires, arrêter les notifications et archiver l\'appli"</string> - <string name="auto_revoke_summary" msgid="5867548789805911683">"Afin de protéger vos données, les autorisations pour cette application seront retirées si elle n\'est pas utilisée pendant quelques mois."</string> - <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Afin de protéger vos données, si l\'application n\'est pas utilisée pendant quelques mois, les autorisations suivantes seront supprimées : <xliff:g id="PERMS">%1$s</xliff:g>"</string> - <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Afin de protéger vos données, les autorisations ont été supprimées pour les applications que vous n\'avez pas utilisées depuis quelques mois."</string> - <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Pour accorder les autorisations de nouveau, ouvrez l\'application."</string> - <string name="auto_revoke_disabled" msgid="8697684442991567188">"La suppression automatique des autorisations est actuellement désactivée pour cette application."</string> + <string name="auto_revoke_summary" msgid="5867548789805911683">"Afin de protéger vos données, les autorisations pour cette appli seront retirées si elle n\'est pas utilisée pendant quelques mois."</string> + <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Afin de protéger vos données, si l\'appli n\'est pas utilisée pendant quelques mois, les autorisations suivantes seront supprimées : <xliff:g id="PERMS">%1$s</xliff:g>"</string> + <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Afin de protéger vos données, les autorisations ont été supprimées pour les applis que vous n\'avez pas utilisées depuis quelques mois."</string> + <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Pour accorder les autorisations de nouveau, ouvrez l\'appli."</string> + <string name="auto_revoke_disabled" msgid="8697684442991567188">"La suppression automatique des autorisations est actuellement désactivée pour cette appli."</string> <string name="auto_revocable_permissions_none" msgid="8334929619113991466">"Aucune autorisation révocable automatiquement n\'est présentement accordée"</string> <string name="auto_revocable_permissions_one" msgid="5299112369449458176">"L\'autorisation <xliff:g id="PERM">%1$s</xliff:g> sera supprimée."</string> <string name="auto_revocable_permissions_two" msgid="4874067408752041716">"Les autorisations <xliff:g id="PERM_0">%1$s</xliff:g> et <xliff:g id="PERM_1">%2$s</xliff:g> seront supprimées."</string> @@ -222,29 +223,29 @@ <string name="auto_revoked_app_summary_one" msgid="7093213590301252970">"L\'autorisation <xliff:g id="PERMISSION_NAME">%s</xliff:g> a été supprimée"</string> <string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Les autorisations <xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g> et <xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g> ont été supprimées"</string> <string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"L\'autorisation <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER">%2$s</xliff:g> autres ont été supprimées"</string> - <string name="unused_apps_page_title" msgid="6986983535677572559">"Applications non utilisées"</string> - <string name="unused_apps_page_summary" msgid="1867593913217272155">"Si une application n\'est pas utilisée pendant quelques mois :\n\n• les autorisations sont retirées pour protéger vos données;\n• les notifications sont arrêtées pour économiser la pile;\n• les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour permettre d\'accéder de nouveau aux autorisations et aux notifications, ouvrez l\'application."</string> - <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Si une application n\'est pas utilisée pendant un mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour réactiver les autorisations, ouvrez l\'application."</string> + <string name="unused_apps_page_title" msgid="6986983535677572559">"Applis non utilisées"</string> + <string name="unused_apps_page_summary" msgid="1867593913217272155">"Si une appli n\'est pas utilisée pendant quelques mois :\n\n• les autorisations sont retirées pour protéger vos données;\n• les notifications sont arrêtées pour économiser la pile;\n• les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour permettre d\'accéder de nouveau aux autorisations et aux notifications, ouvrez l\'appli."</string> + <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Si une appli n\'est pas utilisée pendant un mois :\n\n• Les autorisations sont retirées pour protéger vos données.\n• Les fichiers temporaires sont retirés pour libérer de l\'espace de stockage.\n\nPour réactiver les autorisations, ouvrez l\'appli."</string> <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Dernière ouverture : il y a plus de # mois}one{Dernière ouverture : il y a plus de # mois}many{Dernière ouverture : il y a plus de # de mois}other{Dernière ouverture : il y a plus de # mois}}"</string> - <string name="last_opened_summary" msgid="5248984030024968808">"Dernière ouverture de l\'application le <xliff:g id="DATE">%s</xliff:g>"</string> + <string name="last_opened_summary" msgid="5248984030024968808">"Dernière ouverture de l\'appli le <xliff:g id="DATE">%s</xliff:g>"</string> <string name="last_opened_summary_short" msgid="1646067226191176825">"Dernière ouverture : <xliff:g id="DATE">%s</xliff:g>"</string> - <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Si vous autorisez la gestion de tous les fichiers, cette application pourra accéder à tous les fichiers enregistrés dans l\'espace de stockage partagé sur cet appareil ou sur des appareils de stockage connectés, les modifier et les supprimer. L\'application pourra accéder à des fichiers sans vous demander la permission."</string> - <string name="special_file_access_dialog" msgid="583804114020740610">"Autoriser cette application à accéder aux fichiers qui se trouvent sur cet appareil ou sur des appareils de stockage connectés et à les modifier et les supprimer. Cette application pourra accéder à des fichiers sans vous demander la permission."</string> - <string name="permission_description_summary_generic" msgid="5401399408814903391">"Les applications ayant cette autorisation peuvent <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string> - <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Les applications qui possèdent cette autorisation peuvent accéder aux données relatives à vos activités physiques, comme la marche, le vélo, la conduite, le nombre de pas effectués et plus encore"</string> - <string name="permission_description_summary_calendar" msgid="103329982944411010">"Les applications qui possèdent cette autorisation peuvent accéder à votre agenda"</string> - <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Les applications qui possèdent cette autorisation peuvent lire et écrire des journaux d\'appels téléphoniques"</string> - <string name="permission_description_summary_camera" msgid="108004375101882069">"Les applications qui possèdent cette autorisation peuvent prendre des photos et filmer des vidéos"</string> - <string name="permission_description_summary_contacts" msgid="2337798886460408996">"Les applications qui possèdent cette autorisation peuvent accéder à vos contacts"</string> - <string name="permission_description_summary_location" msgid="2817531799933480694">"Les applications qui possèdent cette autorisation peuvent accéder à la position de cet appareil"</string> - <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Les applications qui possèdent cette autorisation peuvent trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string> - <string name="permission_description_summary_microphone" msgid="630834800308329907">"Les applications qui possèdent cette autorisation peuvent enregistrer de l\'audio"</string> - <string name="permission_description_summary_phone" msgid="4515277217435233619">"Les applications qui possèdent cette autorisation peuvent faire des appels téléphoniques et les gérer"</string> - <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Les applications qui possèdent cette autorisation peuvent accéder aux données des capteurs concernant vos signes vitaux"</string> - <string name="permission_description_summary_sms" msgid="725999468547768517">"Les applications qui possèdent cette autorisation peuvent envoyer et afficher des messages texte"</string> - <string name="permission_description_summary_storage" msgid="6575759089065303346">"Les applications qui possèdent cette autorisation peuvent accéder aux photos, aux médias et aux fichiers de votre appareil"</string> - <string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Les applications possédant cette autorisation peuvent accéder aux fichiers musicaux et audio sur cet appareil"</string> - <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Les applications possédant cette autorisation peuvent accéder aux photos et aux vidéos sur cet appareil"</string> + <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Si vous autorisez la gestion de tous les fichiers, cette appli pourra accéder à tous les fichiers enregistrés dans l\'espace de stockage partagé sur cet appareil ou sur des appareils de stockage connectés, les modifier et les supprimer. L\'appli pourra accéder à des fichiers sans vous demander la permission."</string> + <string name="special_file_access_dialog" msgid="583804114020740610">"Autoriser cette appli à accéder aux fichiers qui se trouvent sur cet appareil ou sur des appareils de stockage connectés et à les modifier et les supprimer. Cette appli pourra accéder à des fichiers sans vous demander la permission."</string> + <string name="permission_description_summary_generic" msgid="5401399408814903391">"Les applis ayant cette autorisation peuvent <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string> + <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Les applis qui possèdent cette autorisation peuvent accéder aux données relatives à vos activités physiques, comme la marche, le vélo, la conduite, le nombre de pas effectués et plus encore"</string> + <string name="permission_description_summary_calendar" msgid="103329982944411010">"Les applis qui possèdent cette autorisation peuvent accéder à votre agenda"</string> + <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Les applis qui possèdent cette autorisation peuvent lire et écrire des journaux d\'appels téléphoniques"</string> + <string name="permission_description_summary_camera" msgid="108004375101882069">"Les applis qui possèdent cette autorisation peuvent prendre des photos et filmer des vidéos"</string> + <string name="permission_description_summary_contacts" msgid="2337798886460408996">"Les applis qui possèdent cette autorisation peuvent accéder à vos contacts"</string> + <string name="permission_description_summary_location" msgid="2817531799933480694">"Les applis qui possèdent cette autorisation peuvent accéder à la position de cet appareil"</string> + <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Les applis qui possèdent cette autorisation peuvent trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string> + <string name="permission_description_summary_microphone" msgid="630834800308329907">"Les applis qui possèdent cette autorisation peuvent enregistrer de l\'audio"</string> + <string name="permission_description_summary_phone" msgid="4515277217435233619">"Les applis qui possèdent cette autorisation peuvent faire des appels téléphoniques et les gérer"</string> + <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Les applis qui possèdent cette autorisation peuvent accéder aux données des capteurs concernant vos signes vitaux"</string> + <string name="permission_description_summary_sms" msgid="725999468547768517">"Les applis qui possèdent cette autorisation peuvent envoyer et afficher des messages texte"</string> + <string name="permission_description_summary_storage" msgid="6575759089065303346">"Les applis qui possèdent cette autorisation peuvent accéder aux photos, aux médias et aux fichiers de votre appareil"</string> + <string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Les applis possédant cette autorisation peuvent accéder aux fichiers musicaux et audio sur cet appareil"</string> + <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Les applis possédant cette autorisation peuvent accéder aux photos et aux vidéos sur cet appareil"</string> <string name="app_permission_most_recent_summary" msgid="4292074449384040590">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Autorisation présentement refusée/Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Aucun accès"</string> @@ -258,65 +259,65 @@ <string name="denied_header" msgid="903209608358177654">"Non autorisées"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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="days" msgid="609563020985571393">"{count,plural, =1{1 jour}one{# jour}many{# de jours}other{# jours}}"</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="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> + <string name="auto_revoke_permission_reminder_notification_title_one" msgid="6690347469376854137">"1 appli non utilisée"</string> + <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis non utilisées"</string> <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Les autorisations ont été supprimées afin de protéger votre confidentialité. Touchez pour examiner"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Autoris. suppr. pour applis non utilisées"</string> - <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Certaines applications n\'ont pas été utilisées depuis quelques mois. Touchez pour examiner."</string> - <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# application non utilisée}one{# application non utilisée}many{# applications non utilisées}other{# applications non utilisées}}"</string> + <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Certaines applis n\'ont pas été utilisées depuis quelques mois. Touchez pour examiner."</string> + <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# appli non utilisée}one{# appli non utilisée}many{# applis non utilisées}other{# applis non utilisées}}"</string> <string name="unused_apps_notification_content" msgid="9195026773244581246">"Les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été arrêtées. Touchez pour examiner."</string> - <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Voir les applications qui n\'ont plus leurs autorisations"</string> - <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Pour les applications que vous n\'avez pas utilisées depuis un moment, les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été désactivées."</string> - <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Voir les applications"</string> + <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Voir les applis qui n\'ont plus leurs autorisations"</string> + <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Pour les applis que vous n\'avez pas utilisées depuis un moment, les autorisations et les fichiers temporaires ont été retirés, et les notifications ont été désactivées."</string> + <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Voir les applis"</string> <string name="post_drive_permission_decision_reminder_title" msgid="1290697371418139976">"Vérifiez les autorisations récentes"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Pendant la conduite, vous avez donné à <xliff:g id="APP">%1$s</xliff:g> l\'accès à <xliff:g id="PERMISSION">%2$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Pendant la conduite, vous avez donné à <xliff:g id="APP">%1$s</xliff:g> l\'accès à <xliff:g id="PERMISSION_1">%2$s</xliff:g> et à <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Pendant la conduite, vous avez accordé <xliff:g id="COUNT">%1$d</xliff:g> autorisations à <xliff:g id="APP">%2$s</xliff:g>"</string> - <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_0">%1$s</xliff:g> et à # autre application}one{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autre application}many{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applications}other{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applications}}"</string> + <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_0">%1$s</xliff:g> et à # autre appli}one{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autre appli}many{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applis}other{Pendant la conduite, vous avez donné l\'accès à <xliff:g id="APP_1">%1$s</xliff:g> et à # autres applis}}"</string> <string name="go_to_settings" msgid="1053735612211228335">"Accéder aux paramètres"</string> - <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Certaines applications n\'ont pas été utilisées depuis quelques mois"</string> + <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Certaines applis n\'ont pas été utilisées depuis quelques mois"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"Autorisations supprimées"</string> <string name="permission_removed_page_title" msgid="2627436155091001209">"Autorisations supprimées"</string> - <string name="all_unused_apps_category_title" msgid="755663524704745414">"Toutes les applications non utilisées"</string> + <string name="all_unused_apps_category_title" msgid="755663524704745414">"Toutes les applis non utilisées"</string> <string name="months_ago" msgid="1766026492610646354">"Il y a <xliff:g id="COUNT">%1$d</xliff:g> mois"</string> <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"Les autorisations ont été supprimées afin de protéger votre confidentialité"</string> - <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"L\'application <xliff:g id="APP_NAME">%s</xliff:g> a accédé à votre position en arrière-plan"</string> - <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Cette application peut toujours accéder à votre position. Touchez l\'écran pour modifier cela."</string> - <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Voir l\'application ayant accès à vos notifications"</string> + <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"L\'appli <xliff:g id="APP_NAME">%s</xliff:g> a accédé à votre position en arrière-plan"</string> + <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Cette appli peut toujours accéder à votre position. Touchez l\'écran pour modifier cela."</string> + <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Voir l\'appli ayant accès à vos notifications"</string> <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> peut ignorer et gérer le contenu de vos notifications de même qu\'y accéder"</string> - <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Cette application peut ignorer le contenu de vos notifications, y accéder et le gérer. Certaines applications ont besoin de cet accès pour fonctionner correctement."</string> + <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Cette appli peut ignorer le contenu de vos notifications, y accéder et le gérer. Certaines applis ont besoin de cet accès pour fonctionner correctement."</string> <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Retirer l\'accès"</string> <string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"Afficher d\'autres options"</string> <string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"Accès retiré"</string> - <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Vérifier l\'application ayant un accès complet à l\'appareil"</string> - <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> peut voir votre écran et effectuer des actions sur votre appareil. Les applications d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement."</string> - <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Cette application peut voir votre écran et effectuer des actions sur votre appareil. Les applications d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement, mais vérifiez d\'abord l\'application et assurez-vous qu\'elle est fiable."</string> + <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Vérifier l\'appli ayant un accès complet à l\'appareil"</string> + <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> peut voir votre écran et effectuer des actions sur votre appareil. Les applis d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement."</string> + <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"Cette appli peut voir votre écran et effectuer des actions sur votre appareil. Les applis d\'accessibilité ont besoin de ce type d\'accès pour fonctionner correctement, mais vérifiez d\'abord l\'appli et assurez-vous qu\'elle est fiable."</string> <string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Retirer l\'accès"</string> - <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Voir les applications ayant un accès complet"</string> + <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"Voir les applis ayant un accès complet"</string> <string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"Accès retiré"</string> <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Système Android"</string> <string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"Autorisations supprimées pour protéger votre confidentialité"</string> - <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"L\'application <xliff:g id="APP_NAME">%s</xliff:g> n\'a pas été utilisée depuis plusieurs mois. Touchez pour examiner."</string> - <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> et une autre application n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string> - <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> autres applications n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string> - <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"Une application est inutilisée"</string> - <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications sont inutilisées"</string> + <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"L\'appli <xliff:g id="APP_NAME">%s</xliff:g> n\'a pas été utilisée depuis plusieurs mois. Touchez pour examiner."</string> + <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> et une autre appli n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string> + <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> et <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> autres applis n\'ont pas été utilisées depuis plusieurs mois. Touchez pour examiner."</string> + <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"Une appli est inutilisée"</string> + <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis sont inutilisées"</string> <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"Les autorisations seront supprimées pour protéger votre confidentialité. Touchez pour examiner."</string> - <string name="unused_apps_title" msgid="8589298917717872239">"Applications non utilisées"</string> + <string name="unused_apps_title" msgid="8589298917717872239">"Applis non utilisées"</string> <string name="unused_apps_subtitle_after" msgid="2034267519506357898">"Les autorisations ont été supprimées pour"</string> <string name="unused_apps_subtitle_before" msgid="5233302577076132427">"Les autorisations seront supprimées pour"</string> <string name="unused_permissions_subtitle_two" msgid="2207266295008423015">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g> et <xliff:g id="PERM_NAME_1">%2$s</xliff:g>"</string> <string name="unused_permissions_subtitle_many" msgid="4387289202207450238">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g>, <xliff:g id="PERM_NAME_1">%2$s</xliff:g> et <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g> autres"</string> - <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Pour protéger vos données, les autorisations ont été supprimées pour les applications que vous n\'avez pas utilisées depuis plusieurs mois"</string> - <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Pour protéger vos données, les autorisations ont été supprimées pour certaines applications que vous n\'avez pas utilisées depuis plusieurs mois"</string> - <string name="one_unused_app_summary" msgid="7831913934488881991">"1 application n\'a pas été utilisée depuis quelques mois"</string> - <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applications n\'ont pas été utilisées depuis quelques mois"</string> - <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Uniquement lorsque l\'application est en cours d\'utilisation"</string> + <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"Pour protéger vos données, les autorisations ont été supprimées pour les applis que vous n\'avez pas utilisées depuis plusieurs mois"</string> + <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"Pour protéger vos données, les autorisations ont été supprimées pour certaines applis que vous n\'avez pas utilisées depuis plusieurs mois"</string> + <string name="one_unused_app_summary" msgid="7831913934488881991">"1 appli n\'a pas été utilisée depuis quelques mois"</string> + <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> applis n\'ont pas été utilisées depuis quelques mois"</string> + <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Uniquement lorsque l\'appli est en cours d\'utilisation"</string> <string name="permission_subtitle_media_only" msgid="8917869683764720717">"Éléments multimédias"</string> <string name="permission_subtitle_all_files" msgid="4982613338298067862">"Tous les fichiers"</string> <string name="permission_subtitle_background" msgid="8916750995309083180">"Autorisée en permanence"</string> @@ -327,7 +328,7 @@ <string name="app_perms_content_provider_7d" msgid="3215454898257814868">"Dernier accès : au cours des 7 derniers jours"</string> <string name="app_perms_24h_access_background" msgid="3413674718969576843">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g> • Autorisée en permanence"</string> <string name="app_perms_24h_access_yest_background" msgid="9174750810998076725">"Dernier accès : hier à <xliff:g id="TIME_DATE">%1$s</xliff:g> • Autorisée en permanence"</string> - <string name="app_perms_7d_access_background" msgid="408099213372185627">"Dernier accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Autorisée en permanence"</string> + <string name="app_perms_7d_access_background" msgid="408099213372185627">"Dernier accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Autorisée en permanence"</string> <string name="app_perms_content_provider_24h_background" msgid="3825902995186961496">"Dernier accès : au cours des dernières 24 h • Autorisée en permanence"</string> <string name="app_perms_content_provider_7d_background" msgid="4818839672116463542">"Dernier accès : au cours des 7 derniers j. • Autorisée en permanence"</string> <string name="app_perms_24h_access_media_only" msgid="6651699644199132054">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g> • Fichiers multimédias"</string> @@ -342,76 +343,76 @@ <string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"Dernier accès : au cours des 7 derniers jours • Tous les fichiers"</string> <string name="no_permissions_allowed" msgid="6081976856354669209">"Aucune autorisation accordée"</string> <string name="no_permissions_denied" msgid="8159923922804043282">"Aucune autorisation refusée"</string> - <string name="no_apps_allowed" msgid="7718822655254468631">"Aucune application autorisée"</string> - <string name="no_apps_allowed_full" msgid="8011716991498934104">"Aucune application autorisée pour tous les fichiers"</string> - <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Aucune application autorisée pour uniquement le contenu multimédia"</string> - <string name="no_apps_denied" msgid="7663435886986784743">"Aucune application refusée"</string> + <string name="no_apps_allowed" msgid="7718822655254468631">"Aucune appli autorisée"</string> + <string name="no_apps_allowed_full" msgid="8011716991498934104">"Aucune appli autorisée pour tous les fichiers"</string> + <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"Aucune appli autorisée pour uniquement le contenu multimédia"</string> + <string name="no_apps_denied" msgid="7663435886986784743">"Aucune appli refusée"</string> <string name="car_permission_selected" msgid="180837028920791596">"Sélectionnée"</string> <string name="settings" msgid="5409109923158713323">"Paramètres"</string> <string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a un accès complet à votre appareil"</string> - <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> applications d\'accessibilité ont un accès complet à votre appareil"</string> + <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> applis d\'accessibilité ont un accès complet à votre appareil"</string> <string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> peut voir votre écran, vos actions et ce que vous entrez; il peut également effectuer des actions et contrôler l\'écran."</string> - <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Ces applications peuvent voir votre écran, vos actions et ce que vous entrez; elles peuvent également effectuer des actions et contrôler l\'écran."</string> + <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"Ces applis peuvent voir votre écran, vos actions et ce que vous entrez; elles peuvent également effectuer des actions et contrôler l\'écran."</string> <string name="role_assistant_label" msgid="4727586018198208128">"Assistant numérique par défaut"</string> <string name="role_assistant_short_label" msgid="3369003713187703399">"Appli d\'assistant numérique"</string> - <string name="role_assistant_description" msgid="6622458130459922952">"Les applications d\'assistance peuvent vous aider en fonction de l\'information affichée à l\'écran. Certaines applications sont compatibles à la fois avec le lanceur d\'applications et les services d\'entrée vocale, vous permettant de bénéficier d\'une assistance intégrée."</string> + <string name="role_assistant_description" msgid="6622458130459922952">"Les applis d\'assistance peuvent vous aider en fonction de l\'information affichée à l\'écran. Certaines applis sont compatibles à la fois avec le lanceur d\'applis et les services d\'entrée vocale, vous permettant de bénéficier d\'une assistance intégrée."</string> <string name="role_browser_label" msgid="2877796144554070207">"Appli de navigation par défaut"</string> - <string name="role_browser_short_label" msgid="6745009127123292296">"Application de navigateur"</string> - <string name="role_browser_description" msgid="3465253637499842671">"Applications qui vous donnent accès à Internet et qui affichent des liens que vous pouvez toucher"</string> - <string name="role_browser_request_title" msgid="2895200507835937192">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour la navigation?"</string> + <string name="role_browser_short_label" msgid="6745009127123292296">"Appli de navigateur"</string> + <string name="role_browser_description" msgid="3465253637499842671">"Applis qui vous donnent accès à Internet et qui affichent des liens que vous pouvez toucher"</string> + <string name="role_browser_request_title" msgid="2895200507835937192">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour la navigation?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"Aucune autorisation nécessaire"</string> <string name="role_dialer_label" msgid="1100224146343237968">"Appli de téléphone par défaut"</string> <string name="role_dialer_short_label" msgid="7186888549465352489">"Appli Téléphone"</string> - <string name="role_dialer_description" msgid="8768708633696539612">"Applications qui vous permettent de faire et de recevoir des appels téléphoniques sur votre appareil"</string> - <string name="role_dialer_request_title" msgid="5959618560705912058">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application de téléphonie par défaut?"</string> - <string name="role_dialer_request_description" msgid="6288839625724909320">"Cette application aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string> + <string name="role_dialer_description" msgid="8768708633696539612">"Applis qui vous permettent de faire et de recevoir des appels téléphoniques sur votre appareil"</string> + <string name="role_dialer_request_title" msgid="5959618560705912058">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de téléphonie par défaut?"</string> + <string name="role_dialer_request_description" msgid="6288839625724909320">"Cette appli aura accès à votre appareil photo, à vos contacts, à votre microphone, à votre téléphone et à vos messages texte"</string> <string name="role_dialer_search_keywords" msgid="3324448983559188087">"composeur"</string> <string name="role_sms_label" msgid="8456999857547686640">"Appli de mess. texte par défaut"</string> - <string name="role_sms_short_label" msgid="4371444488034692243">"Application de messagerie texte"</string> - <string name="role_sms_description" msgid="3424020199148153513">"Applications qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des messages texte, des photos, des vidéos et plus encore"</string> - <string name="role_sms_request_title" msgid="7953552109601185602">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application de messagerie texte par défaut?"</string> - <string name="role_sms_request_description" msgid="2691004766132144886">"Cette application aura accès à votre appareil photo, à vos contacts, à vos fichiers, à vos documents, à votre microphone, à votre téléphone et à vos messages texte"</string> + <string name="role_sms_short_label" msgid="4371444488034692243">"Appli de messagerie texte"</string> + <string name="role_sms_description" msgid="3424020199148153513">"Applis qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des messages texte, des photos, des vidéos et plus encore"</string> + <string name="role_sms_request_title" msgid="7953552109601185602">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de messagerie texte par défaut?"</string> + <string name="role_sms_request_description" msgid="2691004766132144886">"Cette appli aura accès à votre appareil photo, à vos contacts, à vos fichiers, à vos documents, à votre microphone, à votre téléphone et à vos messages texte"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"message texte, envoyer des messages, messages, messagerie"</string> - <string name="role_emergency_label" msgid="7028825857206842366">"Application d\'urgence par défaut"</string> - <string name="role_emergency_short_label" msgid="2388431453335350348">"Application d\'urgence"</string> - <string name="role_emergency_description" msgid="5051840234887686630">"Applications qui vous permettent d\'enregistrer vos données médicales et de les rendre accessibles aux intervenants d\'urgence; pour recevoir des alertes sur des événements météorologiques graves et des désastres naturels; pour avertir d\'autres personnes lorsque vous avez besoin d\'aide"</string> - <string name="role_emergency_request_title" msgid="8469579020654348567">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour les urgences?"</string> + <string name="role_emergency_label" msgid="7028825857206842366">"Appli d\'urgence par défaut"</string> + <string name="role_emergency_short_label" msgid="2388431453335350348">"Appli d\'urgence"</string> + <string name="role_emergency_description" msgid="5051840234887686630">"Applis qui vous permettent d\'enregistrer vos données médicales et de les rendre accessibles aux intervenants d\'urgence; pour recevoir des alertes sur des événements météorologiques graves et des désastres naturels; pour avertir d\'autres personnes lorsque vous avez besoin d\'aide"</string> + <string name="role_emergency_request_title" msgid="8469579020654348567">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour les urgences?"</string> <string name="role_emergency_request_description" msgid="131645948770262850">"Aucune autorisation nécessaire"</string> <string name="role_emergency_search_keywords" msgid="1920007722599213358">"En cas d\'urgence"</string> <string name="role_home_label" msgid="3871847846649769412">"Appli d\'accueil par défaut"</string> - <string name="role_home_short_label" msgid="8544733747952272337">"Application sur l\'écran d\'accueil"</string> - <string name="role_home_description" msgid="7997371519626556675">"Applications qui remplacent les écrans d\'accueil sur votre appareil Android et qui vous donnent accès au contenu et aux fonctionnalités de votre appareil"</string> - <string name="role_home_request_title" msgid="738136983453341081">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application d\'écran d\'accueil par défaut?"</string> + <string name="role_home_short_label" msgid="8544733747952272337">"Appli sur l\'écran d\'accueil"</string> + <string name="role_home_description" msgid="7997371519626556675">"Applis qui remplacent les écrans d\'accueil sur votre appareil Android et qui vous donnent accès au contenu et aux fonctionnalités de votre appareil"</string> + <string name="role_home_request_title" msgid="738136983453341081">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli d\'écran d\'accueil par défaut?"</string> <string name="role_home_request_description" msgid="2658833966716057673">"Aucune autorisation nécessaire"</string> - <string name="role_home_search_keywords" msgid="3830755001192666285">"lanceur d\'applications"</string> + <string name="role_home_search_keywords" msgid="3830755001192666285">"lanceur d\'applis"</string> <string name="role_call_redirection_label" msgid="5785304207206147590">"Appli redirect. appel par déf."</string> <string name="role_call_redirection_short_label" msgid="7568143419571217757">"Appeler l\'appli. de redirection"</string> - <string name="role_call_redirection_description" msgid="6091669882014664420">"Applications qui vous permettent de transférer des appels sortants vers un autre numéro de téléphone"</string> - <string name="role_call_redirection_request_title" msgid="2816244455003562925">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour la redirection des appels?"</string> + <string name="role_call_redirection_description" msgid="6091669882014664420">"Applis qui vous permettent de transférer des appels sortants vers un autre numéro de téléphone"</string> + <string name="role_call_redirection_request_title" msgid="2816244455003562925">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour la redirection des appels?"</string> <string name="role_call_redirection_request_description" msgid="3118895714178527164">"Aucune autorisation nécessaire"</string> <string name="role_call_screening_label" msgid="883935222060878724">"Appli d\'identif. et de filtrage par défaut"</string> <string name="role_call_screening_short_label" msgid="2048465565063130834">"Appli identif. et filtr. appels"</string> - <string name="role_call_screening_description" msgid="2349431420497468981">"Applications qui vous permettent d\'identifier les appelants et de bloquer les appels indésirables, les appels automatisés et les numéros indésirables"</string> - <string name="role_call_screening_request_title" msgid="7358309224566977290">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour l\'identification et le filtrage des appelants?"</string> + <string name="role_call_screening_description" msgid="2349431420497468981">"Applis qui vous permettent d\'identifier les appelants et de bloquer les appels indésirables, les appels automatisés et les numéros indésirables"</string> + <string name="role_call_screening_request_title" msgid="7358309224566977290">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli par défaut pour l\'identification et le filtrage des appelants?"</string> <string name="role_call_screening_request_description" msgid="7338511921032446006">"Aucune autorisation nécessaire"</string> - <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Application de navigation par défaut"</string> - <string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Application de navigation"</string> - <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Applications prenant en charge la recherche par points d\'intérêt et la navigation avec itinéraire détaillé"</string> - <string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Définir l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> en tant qu\'application de navigation par défaut?"</string> + <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Appli de navigation par défaut"</string> + <string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"Appli de navigation"</string> + <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Applis prenant en charge la recherche par points d\'intérêt et la navigation avec itinéraire détaillé"</string> + <string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Définir l\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> en tant qu\'appli de navigation par défaut?"</string> <string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"Aucune autorisation nécessaire"</string> <string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et d\'accéder aux autorisations pour votre téléphone, vos messages texte, vos contacts et votre agenda."</string> - <string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et de diffuser vos applications à l\'appareil connecté."</string> + <string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et de diffuser vos applis à l\'appareil connecté."</string> <string name="role_companion_device_computer_description" msgid="416099879217066377">"Ce service partage vos photos, vos contenus multimédias et vos notifications de votre téléphone vers d\'autres appareils."</string> <string name="role_notes_label" msgid="7451627001058089536">"Appli prise de notes par défaut"</string> - <string name="role_notes_short_label" msgid="8796604147546125285">"Application de prise de notes"</string> - <string name="role_notes_description" msgid="8496852798616883551">"Applications qui vous permettent de prendre des notes sur votre appareil"</string> + <string name="role_notes_short_label" msgid="8796604147546125285">"Appli de prise de notes"</string> + <string name="role_notes_description" msgid="8496852798616883551">"Applis qui vous permettent de prendre des notes sur votre appareil"</string> <string name="role_notes_search_keywords" msgid="7710756695666744631">"remarques"</string> <string name="role_wallet_label" msgid="3719419175656204207">"Appli portefeuille par défaut"</string> - <string name="role_wallet_short_label" msgid="6521288403762457452">"Application de portefeuille"</string> + <string name="role_wallet_short_label" msgid="6521288403762457452">"Appli de portefeuille"</string> <string name="role_wallet_description" msgid="3726535836165949838">"Les applis de portefeuille peuvent stocker vos cartes de crédit et de fidélité, vos clés de voiture et d\'autres données pour faciliter différentes modalités de transaction."</string> - <string name="role_wallet_request_title" msgid="4770217108262737093">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application de portefeuille par défaut?"</string> + <string name="role_wallet_request_title" msgid="4770217108262737093">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de portefeuille par défaut?"</string> <string name="role_wallet_request_description" msgid="6305487425777483053">"Aucune autorisation nécessaire"</string> - <string name="request_role_current_default" msgid="738722892438247184">"Application par défaut actuelle"</string> + <string name="request_role_current_default" msgid="738722892438247184">"Appli par défaut actuelle"</string> <string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne plus me demander"</string> <string name="request_role_set_as_default" msgid="4253949643984172880">"Définir par défaut"</string> <string name="phone_call_uses_microphone" msgid="233569591461187177">"Le micro est utilisé pour un <b>appel téléphonique</b>"</string> @@ -428,40 +429,41 @@ <string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Utilisation récente du microphone et de l\'appareil photo"</string> <string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string> <string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" et "</string> - <string name="default_app_search_keyword" msgid="8330125736889689743">"applications par défaut"</string> + <string name="default_app_search_keyword" msgid="8330125736889689743">"applis par défaut"</string> <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone et appareil photo"</string> <string name="settings_button" msgid="4414988414732479636">"Paramètres"</string> - <string name="default_apps" msgid="5119201969348748639">"Applications par défaut"</string> - <string name="no_default_apps" msgid="2593466527182950231">"Aucune application par défaut"</string> - <string name="default_apps_more" msgid="4078194675848858093">"Autres applications par défaut"</string> + <string name="default_apps" msgid="5119201969348748639">"Applis par défaut"</string> + <string name="no_default_apps" msgid="2593466527182950231">"Aucune appli par défaut"</string> + <string name="default_apps_more" msgid="4078194675848858093">"Autres applis par défaut"</string> <string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Ouverture des liens"</string> <string name="default_apps_for_work" msgid="4970308943596201811">"Par défaut pour util. profess."</string> - <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Applications par défaut pour l\'Espace privé"</string> + <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Applis par défaut pour l\'Espace privé"</string> <string name="default_app_none" msgid="9084592086808194457">"Aucune"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Paramètre(s) système par défaut)"</string> - <string name="default_app_no_apps" msgid="115720991680586885">"Aucune application"</string> + <string name="default_app_no_apps" msgid="115720991680586885">"Aucune appli"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Autres services CCP"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Sélectionnée"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Sélectionnée – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> - <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accès spécial des applications"</string> + <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accès spécial des applis"</string> <string name="special_app_access" msgid="5019319067120213797">"Accès spécial des applis"</string> <string name="no_special_app_access" msgid="6950277571805106247">"Aucun accès spécial pour applis"</string> - <string name="special_app_access_no_apps" msgid="4102911722787886970">"Aucune application"</string> + <string name="special_app_access_no_apps" msgid="4102911722787886970">"Aucune appli"</string> <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Non compatible avec les profils professionnels"</string> - <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Remarque : Si vous redémarrez votre appareil et que vous avez défini un verrouillage de l\'écran, cette application ne pourra pas démarrer tant que vous n\'avez pas déverrouillé votre appareil"</string> - <string name="assistant_confirmation_message" msgid="7476540402884416212">"L\'assistant pourra accéder aux données des applications en cours d\'utilisation sur votre système, y compris les données visibles à l\'écran ou accessibles au sein des applications."</string> + <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Remarque : Si vous redémarrez votre appareil et que vous avez défini un verrouillage de l\'écran, cette appli ne pourra pas démarrer tant que vous n\'avez pas déverrouillé votre appareil"</string> + <string name="assistant_confirmation_message" msgid="7476540402884416212">"L\'assistant pourra accéder aux données des applis en cours d\'utilisation sur votre système, y compris les données visibles à l\'écran ou accessibles au sein des applis."</string> <string name="incident_report_channel_name" msgid="3144954065936288440">"Partager les données de débogage"</string> <string name="incident_report_notification_title" msgid="4635984625656519773">"Partager des données de débogage détaillées?"</string> <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> souhaite téléverser des données de débogage."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Partager les données de débogage?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Le système a détecté un problème."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> souhaite téléverser un rapport de bogue créé le <xliff:g id="DATE">%2$s</xliff:g> à <xliff:g id="TIME">%3$s</xliff:g> sur cet appareil. Les rapports de bogue contiennent des données personnelles relatives à votre appareil ou enregistrées par des applications, comme des noms d\'utilisateur, des données de localisation, des identifiants d\'appareils et des renseignements relatifs au réseau. Ne partagez les rapports de bogue qu\'avec des personnes et des applications que vous jugez fiables. Autoriser l\'application <xliff:g id="APP_NAME_1">%4$s</xliff:g> à téléverser un rapport de bogue?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"L\'appli <xliff:g id="APP_NAME_0">%1$s</xliff:g> souhaite téléverser un rapport de bogue créé le <xliff:g id="DATE">%2$s</xliff:g> à <xliff:g id="TIME">%3$s</xliff:g> sur cet appareil. Les rapports de bogue contiennent des données personnelles relatives à votre appareil ou enregistrées par des applis, comme des noms d\'utilisateur, des données de localisation, des identifiants d\'appareils et des renseignements relatifs au réseau. Ne partagez les rapports de bogue qu\'avec des personnes et des applis que vous jugez fiables. Autoriser l\'appli <xliff:g id="APP_NAME_1">%4$s</xliff:g> à téléverser un rapport de bogue?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Une erreur s\'est produite lors du traitement du rapport de bogue pour <xliff:g id="APP_NAME">%1$s</xliff:g>, alors le partage des données de débogage détaillées a été refusé. Nous sommes désolés de l\'interruption."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Autoriser"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Refuser"</string> <string name="adjust_user_sensitive_title" msgid="4196724451314280527">"Paramètres avancés"</string> <string name="menu_adjust_user_sensitive" msgid="6497923610654425780">"Paramètres avancés"</string> - <string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"Afficher l\'usage des applications système"</string> - <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"Afficher l\'utilisation des autorisations par les applications système dans la barre d\'état, le tableau de bord et ailleurs"</string> + <string name="adjust_user_sensitive_globally_title" msgid="8649190949066029174">"Afficher l\'usage des applis système"</string> + <string name="adjust_user_sensitive_globally_summary" msgid="129467818433773912">"Afficher l\'utilisation des autorisations par les applis système dans la barre d\'état, le tableau de bord et ailleurs"</string> <string name="adjust_user_sensitive_per_app_header" msgid="4543506440989005648">"Mettre en avant l\'utilisation pour les éléments suivants"</string> <string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Afficher la détection des déclencheurs de l\'assistant"</string> <string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Afficher l\'icône dans la barre d\'état lorsque le microphone est utilisé pour activer l\'assistant vocal"</string> @@ -471,17 +473,17 @@ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos contacts sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_location" msgid="6990232580121067883">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de cet appareil?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'application aura uniquement accès à la position lorsque vous l\'utilisez"</string> + <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'appli aura uniquement accès à la position lorsque vous l\'utilisez"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de cet appareil?"</string> <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Cette appli pourrait demander à accéder à votre position en tout temps, même si vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string> <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Modifier l\'accès à la position pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string> <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Modifier l\'accès à la position pour <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Cette application veut accéder à votre position en tout temps, même lorsque vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string> + <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Cette appli veut accéder à votre position en tout temps, même lorsque vous ne l\'utilisez pas. Accordez cette autorisation dans les "<annotation id="link">"paramètres"</annotation>"."</string> <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à trouver les appareils à proximité, à s\'y connecter et à déterminer leur position relative?"</string> <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à détecter les appareils à proximité, à s\'y connecter et à déterminer leur position relative sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à trouver les appareils à proximité, à s\'y connecter et à déterminer leur position relative? "<annotation id="link">"Accordez l\'autorisation dans les paramètres."</annotation></string> - <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Modifier l\'accès à la position de l\'application <xliff:g id="APP_NAME"><b>%1$s</b></xliff:g> d\'approximative à exacte?"</string> + <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Modifier l\'accès à la position de l\'appli <xliff:g id="APP_NAME"><b>%1$s</b></xliff:g> d\'approximative à exacte?"</string> <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Modifier l\'accès à la position de <b><xliff:g id="APP_NAME"><b>%1$s</b></xliff:g></b> sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> d\'approximative à exacte?"</string> <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position approximative de cet appareil?"</string> <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position approximative de <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> @@ -503,24 +505,24 @@ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à plus de photos et de vidéos sur <b><xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à enregistrer de l\'audio sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'application pourra uniquement enregistrer de l\'audio lorsque vous l\'utilisez"</string> + <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'appli 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> <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à enregistrer de l\'audio sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Cette application pourrait demander à enregistrer de l\'audio en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> + <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Cette appli pourrait demander à enregistrer de l\'audio en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Modifier l\'accès au microphone pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string> <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Modifier l\'accès au microphone pour <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Cette application veut enregistrer de l\'audio en tout temps, même si vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> + <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Cette appli veut enregistrer de l\'audio en tout temps, même si vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos activités physiques?"</string> <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à votre activité physique sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_camera" msgid="5123097035410002594">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à prendre des photos et à enregistrer des vidéos?"</string> <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à prendre des photos et à enregistrer des vidéos sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'application pourra uniquement prendre des photos et enregistrer des vidéos lorsque vous l\'utilisez"</string> + <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'appli pourra uniquement prendre des photos et enregistrer des vidéos lorsque vous l\'utilisez"</string> <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à enregistrer des vidéos?"</string> <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à prendre des photos et à enregistrer des vidéos sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Cette appli pourrait demander à prendre des photos et des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> <string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Modifier l\'accès à l\'appareil photo pour « <xliff:g id="APP_NAME">%1$s</xliff:g> »?"</string> <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Modifier l\'accès à l\'appareil photo pour <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Cette application veut prendre des photos et enregistrer des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> + <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Cette appli veut prendre des photos et enregistrer des vidéos en tout temps, même lorsque vous ne l\'utilisez pas. "<annotation id="link">"Autorisez dans les paramètres."</annotation></string> <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos journaux d\'appels?"</string> <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos journaux d\'appels téléphoniques sur <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_phone" msgid="1829234136997316752">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à faire et à gérer des appels téléphoniques?"</string> @@ -540,9 +542,9 @@ <string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Votre organisation permet à <xliff:g id="APP_NAME">%1$s</xliff:g> d\'accéder à votre position"</string> <string name="other_permissions_label" msgid="8986184335503271992">"Autres autorisations"</string> <string name="not_used_permissions_label" msgid="3939839426115141264">"Autorisations utilisées par le système"</string> - <string name="not_used_permissions_description" msgid="7595514824169388718">"Autorisations utilisées par les applications système."</string> + <string name="not_used_permissions_description" msgid="7595514824169388718">"Autorisations utilisées par les applis système."</string> <string name="additional_permissions_label" msgid="7693557637462569046">"Autorisations supplémentaires"</string> - <string name="additional_permissions_description" msgid="2186611950890732112">"Autorisations définies par les applications."</string> + <string name="additional_permissions_description" msgid="2186611950890732112">"Autorisations définies par les applis."</string> <string name="privdash_label_camera" msgid="1426440033626198096">"Appareil photo"</string> <string name="privdash_label_microphone" msgid="8415035835803511693">"Microphone"</string> <string name="privdash_label_location" msgid="6882400763866489291">"Position"</string> @@ -551,12 +553,12 @@ <string name="privdash_label_24h" msgid="1512532123865375319">"Dernières\n24 heures"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"7 derniers\njours"</string> <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> pour cent"</string> - <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur la barre d\'état de votre tableau de bord de confidentialité."</string> - <string name="exempt_info_label" msgid="6286190981253476699">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur votre tableau de bord de confidentialité."</string> + <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur la barre d\'état de votre tableau de bord de confidentialité."</string> + <string name="exempt_info_label" msgid="6286190981253476699">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> est protégée par Android. Comme vos données sont traitées sur cet appareil, l\'utilisation de l\'autorisation de celle-ci ne s\'affiche pas sur votre tableau de bord de confidentialité."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"L\'appareil photo de l\'appareil est bloqué"</string> <string name="blocked_microphone_title" msgid="1631517143648232585">"Le microphone de l\'appareil est bloqué"</string> <string name="blocked_location_title" msgid="2005608279812892383">"La localisation de l\'appareil est désactivée"</string> - <string name="blocked_sensor_summary" msgid="4443707628305027375">"Pour les applications et les services"</string> + <string name="blocked_sensor_summary" msgid="4443707628305027375">"Pour les applis et les services"</string> <string name="blocked_mic_summary" msgid="8960466941528458347">"Il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string> <string name="blocked_sensor_button_label" msgid="6742092634984289658">"Modifier"</string> <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"L\'accès à l\'appareil photo est désactivé"</string> @@ -586,8 +588,8 @@ <string name="permissions_removed_qs" msgid="8957319130625294572">"L\'autorisation a été retirée"</string> <string name="camera_usage_qs" msgid="4394233566086665994">"Consulter les utilisations récentes de l\'appareil photo"</string> <string name="microphone_usage_qs" msgid="8527666682168170417">"Consulter les utilisations récentes du microphone"</string> - <string name="remove_camera_qs" msgid="3649996161066883350">"Retirer l\'autorisation de cette application"</string> - <string name="remove_microphone_qs" msgid="1276551965129953198">"Retirer l\'autorisation de cette application"</string> + <string name="remove_camera_qs" msgid="3649996161066883350">"Retirer l\'autorisation de cette appli"</string> + <string name="remove_microphone_qs" msgid="1276551965129953198">"Retirer l\'autorisation de cette appli"</string> <string name="manage_service_qs" msgid="7862555549364153805">"Gérer le service"</string> <string name="manage_permissions_qs" msgid="3780541819763475434">"Gérer les autorisations"</string> <string name="active_call_usage_qs" msgid="8559974395932523391">"En cours d\'utilisation par un appel téléphonique"</string> @@ -608,41 +610,41 @@ <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"L\'accès aux photos et aux vidéos ne sera pas non plus autorisé"</string> <string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"L\'accès aux fichiers musicaux et audio sera également autorisé"</string> <string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"L\'accès aux fichiers musicaux et audio ne sera pas non plus autorisé"</string> - <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string> - <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux et audio, elle ne sera pas non plus autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string> - <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio ainsi qu\'à d\'autres fichiers."</string> - <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux photos ni aux vidéos, elle ne sera pas non plus autorisée à accéder aux fichiers musicaux et audio ni à d\'autres fichiers."</string> - <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos et aux vidéos."</string> - <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string> - <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio."</string> - <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Cette application ne prend pas en charge la dernière version d\'Android. Si cette application ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string> - <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Voir les applications ayant accès aux données de localisation en arrière-plan"</string> - <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> peut toujours accéder à votre position, même quand l\'application est fermée"</string> - <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Voir les applications ayant accès aux données de localisation en arrière-plan"</string> - <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Cette application peut toujours accéder à votre position, même lorsqu\'elle est fermée.\n\nCertaines applications de sécurité et d\'urgence requièrent l\'accès à votre position en arrière-plan pour fonctionner correctement."</string> + <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string> + <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux et audio, elle ne sera pas non plus autorisée à accéder aux photos, aux vidéos et à d\'autres fichiers."</string> + <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio ainsi qu\'à d\'autres fichiers."</string> + <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux photos ni aux vidéos, elle ne sera pas non plus autorisée à accéder aux fichiers musicaux et audio ni à d\'autres fichiers."</string> + <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux fichiers musicaux et audio, elle sera également autorisée à accéder aux photos et aux vidéos."</string> + <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string> + <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli peut accéder aux photos et aux vidéos, elle sera également autorisée à accéder aux fichiers musicaux et audio."</string> + <string name="media_confirm_dialog_message_q_to_s_visual_deny" msgid="2145973462806481992">"Cette appli ne prend pas en charge la dernière version d\'Android. Si cette appli ne peut pas accéder aux fichiers musicaux ni audio, elle ne sera pas non plus autorisée à accéder aux photos ni aux vidéos."</string> + <string name="safety_center_background_location_access_notification_title" msgid="8933610618810588237">"Voir les applis ayant accès aux données de localisation en arrière-plan"</string> + <string name="safety_center_background_location_access_reminder_notification_content" msgid="4066560182507301022">"<xliff:g id="APP_NAME">%s</xliff:g> peut toujours accéder à votre position, même quand l\'appli est fermée"</string> + <string name="safety_center_background_location_access_reminder_title" msgid="5477847038103863843">"Voir les applis ayant accès aux données de localisation en arrière-plan"</string> + <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Cette appli peut toujours accéder à votre position, même lorsqu\'elle est fermée.\n\nCertaines applis de sécurité et d\'urgence requièrent l\'accès à votre position en arrière-plan pour fonctionner correctement."</string> <string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Accès modifié"</string> <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Voir les utilisations récentes de la position"</string> <string name="privacy_controls_title" msgid="7605929972256835199">"Paramètres de confidentialité"</string> <string name="camera_toggle_title" msgid="1251201397431837666">"Accès à la caméra"</string> <string name="mic_toggle_title" msgid="2649991093496110162">"Accès au microphone"</string> - <string name="perm_toggle_description" msgid="7801326363741451379">"Pour les applications et les services"</string> - <string name="mic_toggle_description" msgid="9163104307990677157">"Pour les applications et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string> - <string name="location_settings_subtitle" msgid="2328360561197430695">"Voir les applications et les services qui ont accès à votre emplacement"</string> + <string name="perm_toggle_description" msgid="7801326363741451379">"Pour les applis et les services"</string> + <string name="mic_toggle_description" msgid="9163104307990677157">"Pour les applis et les services. Si ce paramètre est désactivé, il est possible que les données du microphone soient partagées lorsque vous appelez un numéro d\'urgence."</string> + <string name="location_settings_subtitle" msgid="2328360561197430695">"Voir les applis et les services qui ont accès à votre emplacement"</string> <string name="show_clip_access_notification_title" msgid="5168467637351109096">"Afficher l\'accès au presse-papiers"</string> - <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Un message s\'affiche lorsque les applications accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string> + <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Un message s\'affiche 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">"Les caractères s\'affichent brièvement pendant la saisie"</string> - <string name="permission_rationale_message_location" msgid="2153841534298068414">"Cette application indique qu\'elle peut partager des données de localisation avec des tiers"</string> + <string name="permission_rationale_message_location" msgid="2153841534298068414">"Cette appli indique qu\'elle peut partager des données de localisation avec des tiers"</string> <string name="permission_rationale_location_title" msgid="2404797182678793506">"Partage des données et localisation"</string> <string name="permission_rationale_data_sharing_source_title" msgid="6874604543125814316">"Provenance des renseignements sur le partage des données"</string> - <string name="permission_rationale_data_sharing_device_manufacturer_message" msgid="7569261218145653185">"Le développeur a fourni des renseignements au fabricant de cet appareil sur la façon dont cette application partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string> - <string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Le développeur a fourni des renseignements à "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" sur la façon dont cette application partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string> + <string name="permission_rationale_data_sharing_device_manufacturer_message" msgid="7569261218145653185">"Le développeur a fourni des renseignements au fabricant de cet appareil sur la façon dont cette appli partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string> + <string name="permission_rationale_data_sharing_source_message" msgid="8330794595417986883">"Le développeur a fourni des renseignements à "<annotation id="link"><annotation id="install_source" example="App Store">"%1$s"</annotation></annotation>" sur la façon dont cette appli partage les données. Le développeur peut mettre à jour ces renseignements au fil du temps."</string> <string name="permission_rationale_location_purpose_title" msgid="5115877143670012618">"L\'appli peut partager des données de loc. pour :"</string> <string name="permission_rationale_permission_data_sharing_varies_title" msgid="9103718980919908316">"Les pratiques de partage de données peuvent varier"</string> - <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge. "<annotation id="link">"En savoir plus sur le partage des données"</annotation></string> - <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge."</string> + <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge. "<annotation id="link">"En savoir plus sur le partage des données"</annotation></string> + <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"Les pratiques relatives aux données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge."</string> <string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"Vos données de localisation"</string> - <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Modifiez les autorisations d\'accès de cette application dans les "<annotation id="link">"paramètres de confidentialité"</annotation></string> + <string name="permission_rationale_permission_settings_message" msgid="631286040979660267">"Modifiez les autorisations d\'accès de cette appli dans les "<annotation id="link">"paramètres de confidentialité"</annotation></string> <string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"le fonctionnement de l\'appli;"</string> <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Les analyses"</string> <string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"les communications du développeur;"</string> @@ -652,11 +654,11 @@ <string name="permission_rationale_purpose_account_management" msgid="2985772421946688879">"la gestion du compte."</string> <string name="app_permission_rationale_message" msgid="8511466916077100713">"Sécurité des données"</string> <string name="app_location_permission_rationale_title" msgid="925420340572401350">"Les données de localisation peuvent être partagées"</string> - <string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Cette application indique qu\'elle peut partager vos données de localisation avec des tiers"</string> + <string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Cette appli indique qu\'elle peut partager vos données de localisation avec des tiers"</string> <string name="data_sharing_updates_title" msgid="7996933386875213859">"Mises à jour des pratiques de partage des données pour la localisation"</string> - <string name="data_sharing_updates_summary" msgid="764113985772233889">"Passez en revue les applications qui ont changé la façon dont elles peuvent partager vos données de localisation"</string> - <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Ces applications ont modifié la façon dont elles peuvent partager vos données de localisation. Elles peuvent ne pas les avoir partagées auparavant, ou peuvent maintenant les partager à des fins d\'annonces ou de marketing."</string> - <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applications ont fourni des renseignements sur leurs pratiques de partage des données à une boutique d\'applications. Ils peuvent les mettre à jour au fil du temps.\n\nLes pratiques de partage des données peuvent varier en fonction de la version de votre application, de son utilisation, de votre région et de votre âge."</string> + <string name="data_sharing_updates_summary" msgid="764113985772233889">"Passez en revue les applis qui ont changé la façon dont elles peuvent partager vos données de localisation"</string> + <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Ces applis ont modifié la façon dont elles peuvent partager vos données de localisation. Elles peuvent ne pas les avoir partagées auparavant, ou peuvent maintenant les partager à des fins d\'annonces ou de marketing."</string> + <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Les développeurs de ces applis ont fourni des renseignements sur leurs pratiques de partage des données à une boutique d\'applis. Ils peuvent les mettre à jour au fil du temps.\n\nLes pratiques de partage des données peuvent varier en fonction de la version de votre appli, de son utilisation, de votre région et de votre âge."</string> <string name="learn_about_data_sharing" msgid="4200480587079488045">"En savoir plus sur le partage des données"</string> <string name="shares_location_with_third_parties" msgid="2278051743742057767">"Vos données de localisation sont maintenant partagées avec des tiers"</string> <string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Vos données de localisation sont maintenant partagées avec des tiers à des fins d\'annonces ou de marketing"</string> @@ -669,10 +671,13 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accès hier (<xliff:g id="TIME_DATE">%1$s</xliff:g>)"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Votre mot de passe à usage unique est le suivant : 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Autoriser les paramètres restreints"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour protéger votre sécurité, ce paramètre n\'est pas accessible actuellement."</string> - <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'application n\'a pas obtenu l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> - <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'application a demandé l\'accès à une autorisation sensible qui pose un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1"><br><br></xliff:g>Il est possible que l\'application ne fonctionne pas correctement sans cette autorisation limitée. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Découvrir comment autoriser l\'accès</a>"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action non proposée lors d\'un appel téléphonique"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Autoriser les applis à installer d\'autres applis n\'est pas permis lors d\'un appel téléphonique.\n\n Les escrocs demandent souvent ce type d\'action lors de conversations téléphoniques, alors ces actions sont bloquées pour vous protéger. Si quelqu\'un que vous ne connaissez pas vous guide dans cette démarche, il pourrait s\'agir d\'une escroquerie."</string> + <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli n\'a pas obtenu l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> + <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'appli a demandé l\'accès à une autorisation sensible qui pose un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1"><br><br></xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans cette autorisation limitée. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Découvrir comment autoriser l\'accès</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli n\'a pas obtenu l\'accès pour avoir le rôle<xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'appli a demandé l\'accès à des autorisations sensibles qui posent un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1"><br><br></xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans ces autorisations limitées. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Découvrir comment autoriser l\'accès</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"L\'appli n\'a pas obtenu l\'accès"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Demande d\'autorisation supprimée"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Cette appli demande des autorisations supplémentaires, mais celles-ci ne peuvent pas être accordées lors d\'une session de diffusion en continu. Accordez d\'abord l\'autorisation de votre téléphone."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pour un appel ou un message texte d\'urgence"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Position envoyée aux services d\'urgence"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Cette appli a accédé à la position de votre appareil lors d\'un appel ou de l\'envoi d\'un message texte à un numéro d\'urgence. Cela peut se produire même si l\'appli ne dispose pas de l\'autorisation de localisation ou si la localisation de l\'appareil est désactivée. "<a href="https://support.google.com/android/answer/9319337">"En savoir plus"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Vérifiez votre téléphone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Impossible de joindre votre téléphone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Réessayer?"</string> </resources> diff --git a/PermissionController/res/values-fr-v35/strings.xml b/PermissionController/res/values-fr-v35/strings.xml new file mode 100644 index 000000000..9a88205ed --- /dev/null +++ b/PermissionController/res/values-fr-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sécurité"</string> +</resources> diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml index 8602cc8c8..e51f0a592 100644 --- a/PermissionController/res/values-fr/strings.xml +++ b/PermissionController/res/values-fr/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"autorisations"</string> <string name="cancel" msgid="8943320028373963831">"Annuler"</string> <string name="back" msgid="6249950659061523680">"Retour"</string> + <string name="dialog_close" msgid="6840699812532384661">"Fermer"</string> <string name="available" msgid="6007778121920339498">"Disponible"</string> <string name="blocked" msgid="9195547604866033708">"Bloqué"</string> <string name="on" msgid="280241003226755921">"Activé"</string> @@ -59,7 +60,7 @@ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"Autoriser la gestion de tous les fichiers"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"Autoriser l\'accès aux fichiers multimédias"</string> <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"Applications"</string> - <string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string> + <string name="app_permissions" msgid="3369917736607944781">"Autorisations de l\'application"</string> <string name="unused_apps" msgid="2058057455175955094">"Applications inutilisées"</string> <string name="edit_photos_description" msgid="5540108003480078892">"Modifier les photos sélectionnées pour cette application"</string> <string name="no_unused_apps" msgid="12809387670415295">"Aucune appli inutilisée"</string> @@ -368,7 +369,7 @@ <string name="role_dialer_search_keywords" msgid="3324448983559188087">"clavier"</string> <string name="role_sms_label" msgid="8456999857547686640">"Appli de SMS par défaut"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"Appli de SMS"</string> - <string name="role_sms_description" msgid="3424020199148153513">"Applications qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des SMS, des photos, des vidéos et plus encore"</string> + <string name="role_sms_description" msgid="3424020199148153513">"Applications qui vous permettent d\'utiliser votre numéro de téléphone pour envoyer et recevoir des messages, des photos, des vidéos et plus encore"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme application par défaut pour les SMS ?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Cette appli aura accès à votre appareil photo, vos contacts, vos fichiers et contenus multimédias, votre micro, votre téléphone et vos SMS"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, envoyer un SMS, messages, message"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Aucune"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Application système par défaut)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Aucune application"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Autres services NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Sélectionnée"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Sélectionnée – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accès spéciaux des applis"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Dernière consultation : hier, à <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Dernière consultation : <xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Votre mot de passe à usage unique est le suivant : 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Autoriser les paramètres restreints"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour votre sécurité, ce paramètre est actuellement indisponible."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Action non disponible lors d\'un appel téléphonique"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Vous ne pouvez pas autoriser des applis à installer d\'autres applis pendant un appel téléphonique.\n\n Les escrocs demandent souvent d\'effectuer ce type d\'action lors d\'appels téléphoniques. Cette action est donc bloquée pour vous protéger. Si quelqu\'un que vous ne connaissez pas vous demande d\'effectuer cette action, il s\'agit peut-être d\'une escroquerie."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli s\'est vu refuser l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Cette appli a demandé l\'accès à une autorisation sensible susceptible d\'exposer vos informations financières et personnelles à un risque.<xliff:g id="ID_1"><br><br></xliff:g>Si elle ne dispose pas d\'un accès à cette autorisation restreinte, l\'appli peut ne pas fonctionner correctement. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Découvrez comment autoriser l\'accès</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli s\'est vu refuser l\'accès au statut de <xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Demande d\'autorisation supprimée"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Cette appli demande des autorisations supplémentaires, mais il est impossible d\'accorder des autorisations lors d\'une session de streaming. Accordez tout d\'abord l\'autorisation sur votre téléphone."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pour les messages ou les appels d\'urgence"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Position envoyée aux services d\'urgence"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Cette application a accédé à la position de votre appareil lors d\'un appel ou de l\'envoi d\'un message vers un numéro d\'urgence. Cela peut se produire même si l\'application ne dispose pas de l\'autorisation d\'accéder à la position ou lorsque la position de l\'appareil est désactivée. "<a href="https://support.google.com/android/answer/9319337">"En savoir plus"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Vérifier votre téléphone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Impossible de se connecter à votre téléphone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Réessayer ?"</string> </resources> diff --git a/PermissionController/res/values-gl-v35/strings.xml b/PermissionController/res/values-gl-v35/strings.xml new file mode 100644 index 000000000..54a03138f --- /dev/null +++ b/PermissionController/res/values-gl-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Seguranza"</string> +</resources> diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml index 127d9c64f..b4766bf20 100644 --- a/PermissionController/res/values-gl/strings.xml +++ b/PermissionController/res/values-gl/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Atrás"</string> + <string name="dialog_close" msgid="6840699812532384661">"Pechar"</string> <string name="available" msgid="6007778121920339498">"Dispoñible"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Activado"</string> @@ -196,8 +197,8 @@ <string name="approximate_image_description" msgid="938803699637069884">"Localización aproximada"</string> <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar localización precisa"</string> <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Cando a localización precisa está desactivada, as aplicacións poden acceder á túa localización aproximada"</string> - <string name="app_permission_title" msgid="2090897901051370711">"Permiso de <xliff:g id="PERM">%1$s</xliff:g>"</string> - <string name="app_permission_header" msgid="2951363137032603806">"Permiso de acceso desta aplicación a: <xliff:g id="PERM">%1$s</xliff:g>"</string> + <string name="app_permission_title" msgid="2090897901051370711">"Permiso de acceso a <xliff:g id="PERM">%1$s</xliff:g>"</string> + <string name="app_permission_header" msgid="2951363137032603806">"Acceso a <xliff:g id="PERM">%1$s</xliff:g> para esta aplicación"</string> <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Esta aplicación ten o seguinte permiso de acceso en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>: <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todos os permisos de <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ver todas as aplicacións que teñen este permiso"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ningunha"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Opción predeterminada do sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Non hai ningunha aplicación"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Outros servizos NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Aplicación seleccionada"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Aplicación seleccionada (<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>)"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"acceso especial das aplicacións"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: onte, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"O teu contrasinal dun só uso é 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir opcións restrinxidas"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opción de configuración restrinxida"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pola túa seguranza, esta opción de configuración non está dispoñible nestes momentos."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acción non dispoñible nas chamadas telefónicas"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Non está permitido que as aplicacións instalen outras aplicacións durante as chamadas telefónicas.\n\n Como os estafadores adoitan solicitar este tipo de accións durante as chamadas telefónicas, bloqueámola para protexerte. Se alguén pretende que realices esta acción, é posible que sexa unha estafa."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Denegóuselle á aplicación o acceso ao permiso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A aplicación solicitou acceso a un permiso confidencial que pode poñer en perigo a túa información persoal e financeira.<xliff:g id="ID_1"><br><br></xliff:g>É posible que a aplicación non funcione correctamente sen este permiso restrinxido. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Máis información sobre como permitir o acceso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Denegóuselle á aplicación o acceso para actuar como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Eliminouse a solicitude de permiso"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta aplicación solicita permisos adicionais, pero non se poden conceder nunha sesión de reprodución en tempo real. Primeiro tes que dar o permiso no teu teléfono."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para mensaxes de texto ou chamadas de emerxencia"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Enviouse a localización aos servizos de emerxencia"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta aplicación accedeu á localización do teu dispositivo mentres facías unha chamada ou enviabas unha mensaxe de texto a un número de emerxencia. Isto pode ocorrer mesmo se a aplicación non ten permiso de acceso á localización ou a localización do dispositivo está desactivada. "<a href="https://support.google.com/android/answer/9319337">"Máis información"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Consulta o teléfono"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Non se puido establecer conexión co teléfono"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Queres tentalo de novo?"</string> </resources> diff --git a/PermissionController/res/values-gu-v35/strings.xml b/PermissionController/res/values-gu-v35/strings.xml new file mode 100644 index 000000000..60eb21111 --- /dev/null +++ b/PermissionController/res/values-gu-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"સુરક્ષા"</string> +</resources> diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml index 6a5885f28..b97892c54 100644 --- a/PermissionController/res/values-gu/strings.xml +++ b/PermissionController/res/values-gu/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"પરવાનગીઓ"</string> <string name="cancel" msgid="8943320028373963831">"રદ કરો"</string> <string name="back" msgid="6249950659061523680">"પાછળ"</string> + <string name="dialog_close" msgid="6840699812532384661">"બંધ કરો"</string> <string name="available" msgid="6007778121920339498">"ઉપલબ્ધ છે"</string> <string name="blocked" msgid="9195547604866033708">"બ્લૉક કરેલો છે"</string> <string name="on" msgid="280241003226755921">"ચાલુ છે"</string> @@ -83,7 +84,7 @@ <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"આ ઍપ Androidના જૂના વર્ઝન માટે ડિઝાઇન કરવામાં આવી હતી. જો તમે આ પરવાનગીને મંજૂરી આપશો, તો (ફોટા, વીડિયો, મ્યુઝિક, ઑડિયો અને અન્ય ફાઇલો સહિત) સંપૂર્ણ સ્ટોરેજના ઍક્સેસની મંજૂરી આપવામાં આવશે."</string> <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"આ ઍપ Androidના જૂના વર્ઝન માટે ડિઝાઇન કરવામાં આવી હતી. જો તમે આ પરવાનગી નકારો છો, તો (ફોટા, વીડિયો, મ્યુઝિક, ઑડિયો અને અન્ય ફાઇલો સહિત) સંપૂર્ણ સ્ટોરેજના ઍક્સેસની મંજૂરી નકારવામાં આવશે."</string> <string name="default_permission_description" msgid="4624464917726285203">"અજાણી ક્રિયા કરો"</string> - <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_1">%2$d</xliff:g> માંથી <xliff:g id="COUNT_0">%1$d</xliff:g> ઍપની મંજૂરી છે"</string> + <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_1">%2$d</xliff:g>માંથી <xliff:g id="COUNT_0">%1$d</xliff:g> ઍપને મંજૂરી છે"</string> <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> ઍપને મંજૂરી છે"</string> <string name="menu_show_system" msgid="4254021607027872504">"સિસ્ટમ બતાવો"</string> <string name="menu_hide_system" msgid="3855390843744028465">"સિસ્ટમ છુપાવો"</string> @@ -371,7 +372,7 @@ <string name="role_sms_description" msgid="3424020199148153513">"ઍપ કે જે તમને તમારા ફોન નંબરનો ઉપયોગ કરીને નાના ટેક્સ્ટ મેસેજ, ફોટા, વીડિયો અને ઘણું બધું મોકલવા અને પ્રાપ્ત કરવાની મંજૂરી આપે છે"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g>ને તમારી ડિફૉલ્ટ SMS ઍપ તરીકે સેટ કરીએ?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"આ ઍપને તમારા કૅમેરા, સંપર્કો, ફાઇલો અને મીડિયા, માઇક્રોફોન, ફોન અને SMSનો ઍક્સેસ આપવામાં આવશે."</string> - <string name="role_sms_search_keywords" msgid="8022048144395047352">"ટેક્સ્ટ સંદેશ, ટેક્સ્ટિંગ, સંદેશા, મેસેજિંગ"</string> + <string name="role_sms_search_keywords" msgid="8022048144395047352">"ટેક્સ્ટ મેસેજ, ટેક્સ્ટિંગ, મેસેજ, મેસેજિંગ"</string> <string name="role_emergency_label" msgid="7028825857206842366">"ડિફૉલ્ટ ઇમર્જન્સી ઍપ"</string> <string name="role_emergency_short_label" msgid="2388431453335350348">"ઇમર્જન્સી ઍપ"</string> <string name="role_emergency_description" msgid="5051840234887686630">"ઍપ કે જે તમને તમારી તબીબી માહિતી રેકોર્ડ કરીને ઇમર્જન્સી દરમિયાન ઍક્સેસ કરવાની; હવામાનની ગંભીર ઘટનાઓ અને દુર્ઘટનાઓ વિશે ચેતવણી મેળવવાની અને તમને સહાયની જરૂર હોય ત્યારે બીજા લોકોને તે વિશે સૂચિત કરવાની મંજૂરી આપે છે"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"કોઈ નહીં"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(સિસ્ટમ ડિફૉલ્ટ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"કોઈ ઍપ નથી"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"અન્ય NFC સેવાઓ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"પસંદ કરેલ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"પસંદ કરેલ - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"વિશેષ ઍપનો ઍક્સેસ"</string> @@ -577,7 +579,7 @@ <string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"સુરક્ષા અને પ્રાઇવસીનું સ્ટેટસ. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string> <string name="security_settings" msgid="3808106921175271317">"સુરક્ષા સેટિંગ"</string> <string name="sensor_permissions_qs" msgid="1022267900031317472">"પરવાનગીઓ"</string> - <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"સુરક્ષા અને પ્રાઇવસી"</string> + <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"સિક્યુરિટી અને પ્રાઇવસી"</string> <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"સ્ટેટસ ચેક કરો"</string> <string name="privacy_controls_qs" msgid="5780144882040591169">"તમારી પ્રાઇવસીને લગતા નિયંત્રણો"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"વધુ સેટિંગ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ઍક્સેસ કર્યાનો સમય ગઈકાલે <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ઍક્સેસ કર્યાનો સમય <xliff:g id="TIME_DATE_0">%1$s</xliff:g>ના રોજ <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"તમારો એક-વખત વપરાશનો પાસવર્ડ 132435 છે"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"પ્રતિબંધિત સેટિંગને મંજૂરી આપો"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"પ્રતિબંધિત સેટિંગ"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"તમારી સુરક્ષા માટે, આ સેટિંગ હાલમાં ઉપલબ્ધ નથી."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ફોન કૉલ પર હોવા દરમિયાન આ ક્રિયા ઉપલબ્ધ હોતી નથી"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"કોઈ ફોન કૉલ દરમિયાન, કોઈ એક દ્વારા બીજી ઍપ ઇન્સ્ટૉલ કરવાની મંજૂરી આપવામાં આવતી નથી.\n\n સ્કૅમર દ્વારા ફોન કૉલ પરની વાતચીતો દરમિયાન આ પ્રકારની ક્રિયાની વિનંતી સામાન્ય રીતે કરવામાં આવતી હોય છે, તેથી તમારી સુરક્ષા માટે આને બ્લૉક કરવામાં આવે છે. જો કોઈ અજાણી વ્યક્તિ દ્વારા આ ક્રિયા કરવા માટે તમને માર્ગદર્શન આપવામાં આવતું હોય, તો આ સ્કૅમ હોઈ શકે છે."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ઍપને <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>નો ઍક્સેસ નકારવામાં આવ્યો"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"આ ઍપ દ્વારા કોઈ સંવેદનશીલ માહિતીની પરવાનગીના ઍક્સેસની વિનંતી કરવામાં આવી છે, જેને કારણે તમારી વ્યક્તિગત અને નાણાકીય માહિતી જોખમમાં આવી શકે છે.<xliff:g id="ID_1"><br><br></xliff:g>એ પણ શક્ય છે કે આ પ્રતિબંધિત પરવાનગી વિના ઍપ કદાચ યોગ્ય રીતે કામ ન પણ કરી શકે. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ઍક્સેસ મંજૂર કરવાની રીત જાણો</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ઍપને ડિફૉલ્ટ <xliff:g id="ROLE_NAME">%1$s</xliff:g> બનવાનો ઍક્સેસ નકારવામાં આવ્યો"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ઓકે"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"પરવાનગીની વિનંતી નકારવામાં આવી"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"આ ઍપ વધારાની પરવાનગીઓની વિનંતી કરી રહી છે પણ સ્ટ્રીમિંગ સત્રમાં પરવાનગીઓને મંજૂરી આપી શકાતી નથી. પહેલા તમારા ફોન પર પરવાનગીની મંજૂરી આપો."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ઇમર્જન્સી કૉલ કે ટેક્સ્ટ માટે"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ઇમર્જન્સી સર્વિસને મોકલવામાં આવેલું લોકેશન"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"કોઈ ઇમર્જન્સી નંબરને કૉલ કે ટેક્સ્ટ કરવા દરમિયાન આ ઍપ દ્વારા તમારા ડિવાઇસના લોકેશનને ઍક્સેસ કરવામાં આવ્યું. ઍપને લોકેશનની પરવાનગી ન હોય કે ડિવાઇસનું લોકેશન સેટિંગ બંધ કરેલું હોય, ત્યારે પણ આમ થઈ શકે છે. "<a href="https://support.google.com/android/answer/9319337">"વધુ જાણો"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"તમારો ફોન ચેક કરો"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"તમારા ફોન સુધી પહોંચી શકાયું નથી"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ફરી પ્રયાસ કરીએ?"</string> </resources> diff --git a/PermissionController/res/values-hi-v35/strings.xml b/PermissionController/res/values-hi-v35/strings.xml new file mode 100644 index 000000000..3d8a7dedf --- /dev/null +++ b/PermissionController/res/values-hi-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"सुरक्षा"</string> +</resources> diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml index eb95fb9ba..54dceb35d 100644 --- a/PermissionController/res/values-hi/strings.xml +++ b/PermissionController/res/values-hi/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"अनुमतियां"</string> <string name="cancel" msgid="8943320028373963831">"रद्द करें"</string> <string name="back" msgid="6249950659061523680">"वापस जाएं"</string> + <string name="dialog_close" msgid="6840699812532384661">"बंद करें"</string> <string name="available" msgid="6007778121920339498">"उपलब्ध है"</string> <string name="blocked" msgid="9195547604866033708">"ब्लॉक किया गया"</string> <string name="on" msgid="280241003226755921">"चालू है"</string> @@ -73,13 +74,13 @@ <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{आज}=1{1 दिन पहले}one{# दिन पहले}other{# दिन पहले}}"</string> <string name="app_disable_dlg_positive" msgid="7418444149981904940">"ऐप्लिकेशन बंद करें"</string> <string name="app_disable_dlg_text" msgid="3126943217146120240">"अगर आप इस ऐप्लिकेशन को बंद कर देते हैं, तो हो सकता है कि Android और दूसरे ऐप्लिकेशन ठीक से काम न करें. ध्यान रखें कि आप इस ऐप्लिकेशन को मिटा नहीं सकते, क्योंकि यह आपके डिवाइस पर पहले से इंस्टॉल होकर आया है. इसे बंद करने पर ऐप्लिकेशन बंद हो जाएगा और आपके डिवाइस पर नहीं दिखेगा."</string> - <string name="app_permission_manager" msgid="3903811137630909550">"अनुमतियों को मैनेज करना"</string> + <string name="app_permission_manager" msgid="3903811137630909550">"अनुमतियों को मैनेज करें"</string> <string name="never_ask_again" msgid="4728762438198560329">"दोबारा न पूछें"</string> <string name="no_permissions" msgid="3881676756371148563">"किसी अनुमति की ज़रूरत नहीं है"</string> <string name="additional_permissions" msgid="5801285469338873430">"दूसरी अनुमतियां"</string> <string name="app_permissions_info_button_label" msgid="7633312050729974623">"ऐप्लिकेशन के बारे में जानकारी देखें"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# और अनुमति}one{# और अनुमति}other{# और अनुमतियां}}"</string> - <string name="old_sdk_deny_warning" msgid="2382236998845153919">"यह ऐप्लिकेशन Android के पुराने वर्शन के लिए बनाया गया था. अगर आप अनुमति नहीं देते हैं, तो हो सकता है कि यह ठीक तरह से काम न करे."</string> + <string name="old_sdk_deny_warning" msgid="2382236998845153919">"इस ऐप्लिकेशन को Android के पुराने वर्शन के लिए बनाया गया था. अगर अनुमति नहीं दी जाती है, तो हो सकता है कि यह ठीक तरह से काम न करे."</string> <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"यह ऐप्लिकेशन, Android के पुराने वर्शन के लिए बनाया गया था. यह अनुमति देने पर, ऐप्लिकेशन को आपके डिवाइस में मौजूद सभी फ़ाइलों का ऐक्सेस मिल जाएगा. इसमें फ़ोटो, वीडियो, संगीत, ऑडियो, और दूसरी फ़ाइलें शामिल हैं."</string> <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"यह ऐप्लिकेशन, Android के पुराने वर्शन के लिए बनाया गया था. यह अनुमति न देने पर, ऐप्लिकेशन को आपके डिवाइस में मौजूद किसी भी फ़ाइल का ऐक्सेस नहीं मिलेगा. इसमें फ़ोटो, वीडियो, संगीत, ऑडियो, और दूसरी फ़ाइलें शामिल हैं."</string> <string name="default_permission_description" msgid="4624464917726285203">"ऐसी कार्रवाई करें जिसकी जानकारी नहीं है"</string> @@ -206,7 +207,7 @@ <string name="auto_revoke_label" msgid="5068393642936571656">"ऐप्लिकेशन का इस्तेमाल न होने पर अनुमतियां हटाएं"</string> <string name="unused_apps_label" msgid="2595428768404901064">"अनुमतियां हटाएं और जगह खाली करें"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"इस्तेमाल न होने पर ऐप गतिविधि रोकें"</string> - <string name="unused_apps_label_v3" msgid="693340578642156657">"इस्तेमाल नहीं हुआ ऐप्लिकेशन मैनेज करें"</string> + <string name="unused_apps_label_v3" msgid="693340578642156657">"इस्तेमाल नहीं हो रहा ऐप्लिकेशन मैनेज करें"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"ऐप्लिकेशन की अनुमतियां हटाएं, डिवाइस में कुछ समय के लिए रहने वाली फ़ाइलें मिटाएं, और सूचनाएं रोकें"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ऐप्लिकेशन की अनुमतियां हटाएं, डिवाइस में कुछ समय के लिए रहने वाली फ़ाइलें मिटाएं, सूचनाएं रोकें, और ऐप्लिकेशन संग्रहित करें"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"अगर इस ऐप्लिकेशन का इस्तेमाल कुछ महीनों तक नहीं किया गया, तो इसे दी गई अनुमतियां हटा दी जाएंगी. ऐसा आपके डेटा को सुरक्षित रखने के लिए किया जाएगा."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"कोई नहीं"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डिफ़ॉल्ट)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"कोई ऐप्लिकेशन नहीं"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"अन्य एनएफ़सी सेवाएं"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"चुना हुआ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g> - चुना गया"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ऐप्लिकेशन को खास अनुमति"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"<xliff:g id="TIME_DATE">%1$s</xliff:g> को कल ऐक्सेस किया गया था"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> को ऐक्सेस किया गया"</string> <string name="test_otp_msg" msgid="7559110574222727550">"एक बार इस्तेमाल होने वाला पासवर्ड 132435 है"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"पाबंदी वाली सेटिंग को अनुमति दें"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"इस सेटिंग पर पाबंदी लगाई गई है"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"आपकी सुरक्षा के लिए, यह सेटिंग फ़िलहाल उपलब्ध नहीं है."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फ़ोन कॉल के दौरान, कार्रवाई नहीं की जा सकती"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फ़ोन कॉल के दौरान, ऐप्लिकेशन को अन्य ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है.\n\n फ़ोन पर बातचीत करते हुए, धोखाधड़ी करने वाले लोग अक्सर इस तरह की कार्रवाई करने का अनुरोध करते हैं. इसलिए, आपको सुरक्षित रखने के लिए यह अनुमति नहीं दी जाती है. अगर कोई अनज़ान व्यक्ति, आपको यह कार्रवाई करने के लिए कहता है, तो हो सकता है कि आपके साथ धोखाधड़ी की जा रही हो."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ऐप्लिकेशन को, <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> को ऐक्सेस करने की अनुमति नहीं मिली"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"इस ऐप्लिकेशन ने संवेदनशील जानकारी ऐक्सेस करने का अनुरोध किया है. इसे अनुमति देने पर, आपकी निजी और वित्तीय जानकारी की सुरक्षा को खतरा हो सकता है.<xliff:g id="ID_1"><br><br></xliff:g>हालांकि, हो सकता है कि पाबंदी वाली अनुमति न मिलने पर, ऐप्लिकेशन सही तरह से काम न करें. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ऐक्सेस देने का तरीका जानें</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ऐप्लिकेशन को डिफ़ॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> के तौर पर सेट करने की अनुमति नहीं दी गई"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ठीक है"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"अनुमति का अनुरोध अस्वीकार किया गया"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"यह ऐप्लिकेशन ज़्यादा अनुमतियों का अनुरोध कर रहा है. हालांकि, स्ट्रीमिंग सेशन के दौरान अनुमतियां नहीं दी जा सकतीं. सबसे पहले अपने फ़ोन पर अनुमति दें."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"आपातकालीन कॉल या मैसेज करने के लिए"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"आपातकालीन सेवाओं को जगह की जानकारी भेजी गई"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ऐसा हो सकता है कि किसी आपातकालीन नंबर पर कॉल या मैसेज करने के दौरान, ऐप्लिकेशन ने आपके डिवाइस की जगह की जानकारी ऐक्सेस कर ली हो. ऐप्लिकेशन के पास जगह की जानकारी ऐक्सेस करने की अनुमति न होने या डिवाइस की जगह की जानकारी की सेटिंग बंद होने पर भी, ऐप्लिकेशन ऐसा कर सकता है. "<a href="https://support.google.com/android/answer/9319337">"ज़्यादा जानें"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"अपना फ़ोन देखें"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"आपके फ़ोन से कनेक्ट नहीं किया जा सका"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"फिर से कोशिश करें?"</string> </resources> diff --git a/PermissionController/res/values-hr-v35/strings.xml b/PermissionController/res/values-hr-v35/strings.xml new file mode 100644 index 000000000..71b83b6c9 --- /dev/null +++ b/PermissionController/res/values-hr-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sigurnost"</string> +</resources> diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml index 2061da0e2..7f268858a 100644 --- a/PermissionController/res/values-hr/strings.xml +++ b/PermissionController/res/values-hr/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"dopuštenja"</string> <string name="cancel" msgid="8943320028373963831">"Odustani"</string> <string name="back" msgid="6249950659061523680">"Natrag"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string> <string name="available" msgid="6007778121920339498">"Dostupno"</string> <string name="blocked" msgid="9195547604866033708">"Blokirano"</string> <string name="on" msgid="280241003226755921">"Uključeno"</string> @@ -250,7 +251,7 @@ <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Nikad pristupljeno"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Odbijeno/nikad pristupljeno"</string> <string name="allowed_header" msgid="7769277978004790414">"Imaju dopuštenje"</string> - <string name="allowed_always_header" msgid="6455903312589013545">"Imaju dopuštenje cijelo vrijeme"</string> + <string name="allowed_always_header" msgid="6455903312589013545">"Pristup je dopušten cijelo vrijeme"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"Imaju dopuštenje samo tijekom upotrebe"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Dopušten pristup samo medijima"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Dopušteno upravljanje svim datotekama"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nijedna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Zadana postavka sustava)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Ostale NFC usluge"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Odabrano"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Odabrano – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"poseban pristup za aplikacije"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> želi prenijeti informacije o otklanjanju pogrešaka."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Dijeli podatke o otklanjanju pogrešaka?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistem je otkrio problem."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> zahtijeva prijenos izvješća o programskim pogreškama s ovog uređaja od <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izvješća o programskim pogreškama sadržavaju osobne podatke o uređaju ili one koje su zabilježile aplikacije, kao što su korisnička imena, podaci o lokaciji, identifikatori uređaja i podaci o mreži. Izvješća o programskim pogreškama dijelite samo s osobama i aplikacijama koje smatrate pouzdanim. Dopustiti aplikaciji <xliff:g id="APP_NAME_1">%4$s</xliff:g> prijenos izvješća o programskim pogreškama?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> zahtijeva prijenos izvješća o programskim pogreškama s ovog uređaja od <xliff:g id="DATE">%2$s</xliff:g> u <xliff:g id="TIME">%3$s</xliff:g>. Izvješća o programskim pogreškama sadržavaju osobne podatke o uređaju ili one koje su zabilježile aplikacije, kao što su korisnička imena, podaci o lokaciji, identifikatori uređaja i podaci o mreži. Izvješća o programskim pogreškama dijelite samo s osobama i aplikacijama koje smatrate pouzdanim. Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">%4$s</xliff:g> prijenos izvješća o programskim pogreškama?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Došlo je do pogreške pri obradi izvješća o programskoj pogrešci za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>. Stoga je odbijeno dijeljenje detaljnih podataka o otklanjanju pogrešaka. Ispričavamo se zbog prekida."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Dopusti"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Odbij"</string> @@ -578,7 +580,7 @@ <string name="security_settings" msgid="3808106921175271317">"Sigurnosne postavke"</string> <string name="sensor_permissions_qs" msgid="1022267900031317472">"Dopuštenja"</string> <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Sigurnost i privatnost"</string> - <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Provjera statusa"</string> + <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Provjerite status"</string> <string name="privacy_controls_qs" msgid="5780144882040591169">"Vaše kontrole privatnosti"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"Više postavki"</string> <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Pristup kameri"</string> @@ -669,16 +671,25 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupljeno jučer: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupljeno: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Vaša jednokratna zaporka je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Dopusti ograničene postavke"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti ova postavka trenutačno nije dostupna."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Radnja nije dostupna tijekom telefonskog poziva"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Aplikacijama nije dopušteno instalirati druge aplikacije tijekom telefonskog poziva.\n\n Prevaranti često zahtijevaju tu vrstu radnje tijekom telefonskih razgovora, pa je ona blokirana radi vaše zaštite. Ako vas netko koga ne poznajete navodi na tu radnju, možda je riječ o prijevari."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup dopuštenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> - <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup dopuštenju za osjetljive podatke koje može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je uskraćeno da prema zadanim postavkama bude <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> - <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup dopuštenjima za osjetljive podatke koja mogu ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tih ograničenih dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> + <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup dopuštenju za osjetljive podatke koje može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog uskraćenog dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je uskraćeno da bude zadana <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> + <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup dopuštenjima za osjetljive podatke čime može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tih ograničenih dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji je odbijen pristup"</string> - <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup tom dopuštenju može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> + <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup tom dopuštenju može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1"><br><br></xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog dopuštenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saznajte kako omogućiti pristup</a>"</string> <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"U redu"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Upit za dopuštenje je spriječen"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikacija zahtijeva dodatna dopuštenja, no dopuštenja se ne mogu odobriti u sesiji streaminga. Dopuštenje najprije odobrite na telefonu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za pozive ili poruke hitnim službama"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija poslana hitnim službama"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova je aplikacija pristupila lokaciji vašeg uređaja prilikom upućivanja poziva ili poruke na broj hitne službe. To se može dogoditi čak i kad aplikacija nema dopuštenje za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Provjerite telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nije moguće dohvatiti telefon"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Želite li pokušati ponovo?"</string> </resources> diff --git a/PermissionController/res/values-hu-v35/strings.xml b/PermissionController/res/values-hu-v35/strings.xml new file mode 100644 index 000000000..b0ef5cfc4 --- /dev/null +++ b/PermissionController/res/values-hu-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Biztonság"</string> +</resources> diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml index 8935dc8e2..c1c8b3756 100644 --- a/PermissionController/res/values-hu/strings.xml +++ b/PermissionController/res/values-hu/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"engedélyek"</string> <string name="cancel" msgid="8943320028373963831">"Mégse"</string> <string name="back" msgid="6249950659061523680">"Vissza"</string> + <string name="dialog_close" msgid="6840699812532384661">"Bezárás"</string> <string name="available" msgid="6007778121920339498">"Rendelkezésre áll"</string> <string name="blocked" msgid="9195547604866033708">"Letiltva"</string> <string name="on" msgid="280241003226755921">"Be"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nincs"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Alapértelmezett)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nincs alkalmazás"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Egyéb NFC-szolgáltatások"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Kiválasztva"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Kiválasztva – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"különleges alkalmazás-hozzáférés"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Hozzáférés: tegnap, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Hozzáférés: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Az Ön egyszer használatos jelszava: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Korlátozott beállítások engedélyezése"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Korlátozott beállítás"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Biztonsága érdekében ez a beállítás jelenleg nem használható."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"A művelet nem végezhető el telefonhívás közben"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefonhívás közben nem lehet engedélyezni, hogy alkalmazások más alkalmazásokat telepítsenek.\n\n A csalók gyakran kérik ilyen jellegű műveletek végrehajtását a telefonhívások során, ezért a rendszer az Ön védelme érdekében letiltja ezt a lehetőséget. Ha valaki, akit nem ismer, arra kéri, hogy végezze el ezt a műveletet, előfordulhat, hogy csalárd szándékkal teszi."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Az app nem kapott hozzáférést a következőhöz: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Az alkalmazás hozzáférési engedélyt kért a bizalmas adatokhoz, ami veszélybe sodorhatja az Ön személyes és pénzügyi adatait.<xliff:g id="ID_1"><br><br></xliff:g>Lehetséges, hogy az alkalmazás nem működik megfelelően enélkül a korlátozott engedély nélkül. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>További információ a hozzáférés megadásának módjáról.</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Az app nem kapott hozzáférést a következőhöz: alapértelmezett <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Engedélykérés letiltva"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ez az alkalmazás további engedélyeket kér, de az engedélyeket nem lehet megadni streamelési munkamenetben. Előbb adja meg az engedélyeket a telefonján."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"A segélyhívó szám felhívásakor vagy a segélyhívó számra való üzenetküldéskor"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"A helyadatok el lettek küldve a segélyhívó szolgálatnak"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Az alkalmazás hozzáfért az eszköze helyadataihoz a segélyhívó szám felhívása vagy a segélyhívó számra való üzenetküldés során. Ez akkor is megtörténhet, amikor az appnak nincs helymeghatározási jogosultsága, vagy ki vannak kapcsolva az eszköz helyadatai. "<a href="https://support.google.com/android/answer/9319337">"További információ"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Ellenőrizze telefonját"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nem sikerült elérni a telefonját"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Újrapróbálja?"</string> </resources> diff --git a/PermissionController/res/values-hy-v35/strings.xml b/PermissionController/res/values-hy-v35/strings.xml new file mode 100644 index 000000000..3cbb6bd04 --- /dev/null +++ b/PermissionController/res/values-hy-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Անվտանգություն"</string> +</resources> diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml index 15feb127c..91b3e5bb6 100644 --- a/PermissionController/res/values-hy/strings.xml +++ b/PermissionController/res/values-hy/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"թույլտվություններ"</string> <string name="cancel" msgid="8943320028373963831">"Չեղարկել"</string> <string name="back" msgid="6249950659061523680">"Հետ"</string> + <string name="dialog_close" msgid="6840699812532384661">"Փակել"</string> <string name="available" msgid="6007778121920339498">"Հասանելի է"</string> <string name="blocked" msgid="9195547604866033708">"Արգելափակված է"</string> <string name="on" msgid="280241003226755921">"Միացված է"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Չկա"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Համակարգի կանխադրված հավելված)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Հավելվածներ չկան"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Այլ NFC ծառայություններ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Ընտրված է"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Ընտրված է – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"հատուկ հասանելիություն հավելվածների համար"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ն ուզում է վերբեռնել վրիպազերծման տվյալները:"</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Ուղարկե՞լ վրիպազերծման տվյալները"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Համակարգը խնդիր է հայտնաբերել"</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>-ը թույլտվություն է խնդրում՝ այս սարքից վրիպակների հաշվետվությունը (վերցված <xliff:g id="DATE">%2$s</xliff:g>-ին ժամը <xliff:g id="TIME">%3$s</xliff:g>-ին) վերբեռնելու համար: Վրիպակների հաշվետվությունները ներառում են տվյալներ ձեր սարքի մասին կամ անձնական տվյալներ, որոնք գրանցվել են հավելվածների կողմից, օրինակ՝ օգտատերերի անուններ, տեղադրության մասին տվյալներ, սարքերի ID-ներ և ցանցի մասին տեղեկություններ: Վրիպակների հաշվետվություններով կիսվեք միայն այն մարդկանց ու հավելվածների հետ, որոնց կարող եք վստահել այս տեղեկությունները: Թույլատրե՞լ <xliff:g id="APP_NAME_1">%4$s</xliff:g>-ին վերբեռնել վրիպակների հաշվետվությունը:"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>-ը թույլտվություն է խնդրում՝ <xliff:g id="DATE">%2$s</xliff:g>, ժամը <xliff:g id="TIME">%3$s</xliff:g> ստեղծված վրիպակների հաշվետվությունն այս սարքից վերբեռնելու համար: Վրիպակների հաշվետվությունները ներառում են տվյալներ ձեր սարքի մասին կամ անձնական տվյալներ, որոնք գրանցվել են հավելվածների կողմից, օրինակ՝ օգտատերերի անուններ, տեղադրության մասին տվյալներ, սարքերի ID-ներ և ցանցի մասին տեղեկություններ: Վրիպակների հաշվետվություններով կիսվեք միայն այն մարդկանց ու հավելվածների հետ, որոնց կարող եք վստահել այս տեղեկությունները: Թույլատրե՞լ <xliff:g id="APP_NAME_1">%4$s</xliff:g>-ին վերբեռնել վրիպակների հաշվետվությունը:"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Չհաջողվեց մշակել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածում սխալի մասին տեղեկությունները։ Վրիպազերծման տվյալների ուղարկումը չեղարկվել է։ Հայցում ենք ձեր ներողամտությունը պատճառած անհարմարության համար:"</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Թույլատրել"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Մերժել"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Բացվել է երեկ, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Բացվել է <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Մեկանգամյա օգտագործման ձեր գաղտնաբառը՝ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Թույլատրել սահմանափակ ռեժիմի կարգավորումները"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Սահմանափակումներով կարգավորում"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Անվտանգության նկատառումներից ելնելով՝ այս կարգավորումը ներկայումս անհասանելի է։"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Գործողությունը հասանելի չէ հեռախոսազանգի ժամանակ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Հեռախոսազանգի ժամանակ հավելվածներին չի թույլատրվում տեղադրել այլ հավելվածներ։\n\n Խաբեբաները սովորաբար ցանկանում են այս տեսակի գործողություններն անել հեռախոսային զրույցների ժամանակ, ուստի դա արգելափակված է՝ ձեր անվտանգության նկատառումներից ելնելով։ Եթե որևէ անծանոթ մարդ ձեզանից խնդրել է նմանատիպ թույլտվություն, ամենայն հավանականությամբ դա խարդախություն է։"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Հավելվածին մերժվել է <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ի օգտագործման թույլտվությունը"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Հավելվածը հայցել է կոնֆիդենցիալ տեղեկությունների օգտագործման թույլտվություն, որը կարող է վտանգի ենթարկել ձեր անձնական և ֆինանսական տեղեկությունները։<xliff:g id="ID_1"><br><br></xliff:g>Հնարավոր է, որ առանց այս սահմանափակված թույլտվության՝ հավելվածը չաշխատի պատշաճ կերպով։ <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ինչպես տրամադրել տվյալների օգտագործման թույլտվություն</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Հավելվածին մերժվել է «<xliff:g id="ROLE_NAME">%1$s</xliff:g>» կատեգորիայում կանխադրված լինելու թույլտվությունը"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Եղավ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Թույլտվության հայտն արգելափակվել է"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Այս հավելվածը պահանջում է լրացուցիչ թույլտվություններ, սակայն դրանք հնարավոր չէ տրամադրել հեռարձակման ժամանակ։ Նախ տրամադրեք թույլտվությունը ձեր հեռախոսում։"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Շտապ կանչի կամ հաղորդագրության համար"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Տեղադրության տվյալներն ուղարկվել են արտակարգ իրավիճակների ծառայություններին"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Այս հավելվածին հասանելի է դարձել ձեր սարքի տեղադրությունը արտակարգ իրավիճակների հեռախոսահամարին զանգելու կամ հաղորդագրություն ուղարկելու ժամանակ։ Նման բան կարող է պատահել, նույնիսկ եթե հավելվածը չունի տեղորոշման թույլտվություն, կամ սարքի տեղորոշումն անջատված է։ "<a href="https://support.google.com/android/answer/9319337">"Իմանալ ավելին"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Ստուգեք հեռախոսը"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Չհաջողվեց միանալ հեռախոսին"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Փորձե՞լ նորից։"</string> </resources> diff --git a/PermissionController/res/values-in-v35/strings.xml b/PermissionController/res/values-in-v35/strings.xml new file mode 100644 index 000000000..18a74e175 --- /dev/null +++ b/PermissionController/res/values-in-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Keamanan"</string> +</resources> diff --git a/PermissionController/res/values-in-watch/strings.xml b/PermissionController/res/values-in-watch/strings.xml index c124feda4..4f72c0154 100644 --- a/PermissionController/res/values-in-watch/strings.xml +++ b/PermissionController/res/values-in-watch/strings.xml @@ -22,11 +22,11 @@ <string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Tidak dapat diubah"</string> <string name="generic_yes" msgid="2489207724988649846">"Ya"</string> <string name="generic_cancel" msgid="2631708607129269698">"Batal"</string> - <string name="permission_access_always" msgid="2107115233573823032">"Semua"</string> + <string name="permission_access_always" msgid="2107115233573823032">"Sepanjang waktu"</string> <string name="permission_access_only_foreground" msgid="4412115020089923986">"Saat menggunakan aplikasi"</string> - <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Semua"</string> + <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Sepanjang waktu"</string> <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Saat menggunakan aplikasi"</string> - <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Semua"</string> + <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Sepanjang waktu"</string> <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Saat menggunakan aplikasi"</string> - <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Semua"</string> + <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Sepanjang waktu"</string> </resources> diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml index 74b605607..9ee6d5b8b 100644 --- a/PermissionController/res/values-in/strings.xml +++ b/PermissionController/res/values-in/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"izin"</string> <string name="cancel" msgid="8943320028373963831">"Batal"</string> <string name="back" msgid="6249950659061523680">"Kembali"</string> + <string name="dialog_close" msgid="6840699812532384661">"Tutup"</string> <string name="available" msgid="6007778121920339498">"Tersedia"</string> <string name="blocked" msgid="9195547604866033708">"Diblokir"</string> <string name="on" msgid="280241003226755921">"Aktif"</string> @@ -79,7 +80,7 @@ <string name="additional_permissions" msgid="5801285469338873430">"Izin tambahan"</string> <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Buka info aplikasi"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# lainnya}other{# lainnya}}"</string> - <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Aplikasi ini dirancang untuk versi lama Android. Menolak izin dapat menyebabkan aplikasi tidak berfungsi lagi sesuai harapan."</string> + <string name="old_sdk_deny_warning" msgid="2382236998845153919">"Aplikasi ini dirancang untuk versi lama Android. Menolak izin dapat membuat aplikasi tidak lagi berfungsi sebagaimana mestinya."</string> <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"Aplikasi ini dirancang untuk versi lama Android. Jika Anda memberikan izin ini, akses ke semua penyimpanan (termasuk foto, video, musik, audio, dan file lainnya) akan diizinkan."</string> <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"Aplikasi ini dirancang untuk versi lama Android. Jika Anda menolak izin ini, akses ke semua penyimpanan (termasuk foto, video, musik, audio, dan file lainnya) akan ditolak."</string> <string name="default_permission_description" msgid="4624464917726285203">"melakukan tindakan yang tidak dikenal"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Tidak ada"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Default sistem)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Tidak ada aplikasi"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Layanan NFC lainnya"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Dipilih"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Dipilih - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"akses aplikasi khusus"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Diakses kemarin <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Diakses <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Sandi sekali pakai Anda adalah 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Izinkan setelan terbatas"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setelan terbatas"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Demi keamanan Anda, setelan ini tidak tersedia untuk saat ini."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Tindakan tidak tersedia selama panggilan telepon"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Mengizinkan aplikasi menginstal aplikasi lain tidak diizinkan selama panggilan telepon berlangsung.\n\n Scammer sering meminta jenis tindakan ini selama percakapan panggilan telepon, jadi tindakan ini diblokir untuk melindungi Anda. Jika Anda diarahkan untuk melakukan tindakan ini oleh seseorang yang tidak Anda kenal, hal tersebut mungkin merupakan scam."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikasi ditolak aksesnya ke <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikasi meminta akses ke izin sensitif yang dapat membahayakan info pribadi dan keuangan Anda.<xliff:g id="ID_1"><br><br></xliff:g>Aplikasi mungkin tidak dapat berfungsi dengan baik tanpa izin terbatas ini. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gtPelajari cara mengizinkan akses</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikasi ditolak aksesnya untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> default"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Oke"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permintaan izin diblokir"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikasi ini meminta izin tambahan, tetapi izin tidak dapat diberikan dalam sesi streaming. Berikan izin di ponsel terlebih dahulu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Untuk pesan teks atau panggilan darurat"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokasi dikirim ke layanan darurat"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aplikasi ini mengakses lokasi perangkat Anda saat melakukan panggilan atau mengirim pesan teks ke nomor darurat. Hal ini dapat terjadi bahkan saat aplikasi tidak memiliki izin akses lokasi atau lokasi perangkat dinonaktifkan. "<a href="https://support.google.com/android/answer/9319337">"Pelajari lebih lanjut"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Periksa ponsel Anda"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Tidak dapat terhubung ke ponsel"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Coba lagi?"</string> </resources> diff --git a/PermissionController/res/values-is-v35/strings.xml b/PermissionController/res/values-is-v35/strings.xml new file mode 100644 index 000000000..4463df957 --- /dev/null +++ b/PermissionController/res/values-is-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Öryggi"</string> +</resources> diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml index 08906ba97..bd8d62dad 100644 --- a/PermissionController/res/values-is/strings.xml +++ b/PermissionController/res/values-is/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"heimildir"</string> <string name="cancel" msgid="8943320028373963831">"Hætta við"</string> <string name="back" msgid="6249950659061523680">"Til baka"</string> + <string name="dialog_close" msgid="6840699812532384661">"Loka"</string> <string name="available" msgid="6007778121920339498">"Tiltækt"</string> <string name="blocked" msgid="9195547604866033708">"Lokað á"</string> <string name="on" msgid="280241003226755921">"Kveikt"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ekkert"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sjálfgildi kerfis)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Engin forrit"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Aðrar NFC-þjónustur"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valið"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valið – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"sérstakur forritaaðgangur"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Opnað í gær kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Opnað <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Einnota aðgangsorðið þitt er 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Leyfa takmarkaðar stillingar"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Takmörkuð stilling"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Til að tryggja öryggi þitt er þessi stilling ekki tiltæk eins og er."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Aðgerð ekki í boði á meðan á símtali stendur"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Ekki er heimilt að leyfa forritum að setja önnur forrit upp á meðan á símtali stendur.\n\n Svikarar eiga það til að biðja notendur um að framkvæma aðgerðir á borð við þessa á meðan á samtali stendur. Þar af leiðandi er lokað á hana til að vernda þig. Ef einhver sem þú þekkir ekki bað þig um að framkvæma þessa aðgerð gæti verið um svik að ræða."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Forritið fékk ekki aðgang að <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Forritið bað um aðgang að heimild fyrir viðkvæmu efni sem getur stofnað persónu- og fjármálaupplýsingum þínum í hættu.<xliff:g id="ID_1"><br><br></xliff:g>Forritið virkar hugsanlega ekki sem skyldi án þessarar takmörkuðu heimildar. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Kynntu þér hvernig þú leyfir aðgang</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Forritið fékk ekki aðgang að sjálfgefnu <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Í lagi"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Heimildarbeiðni hafnað"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Þetta forrit biður um viðbótarheimildir en ekki er hægt að veita heimildir í streymislotu. Veittu heimildina í símanum fyrst."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Fyrir neyðarsímtal eða -skilaboð"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Staðsetning send til neyðarþjónustu"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Þetta forrit fékk aðgang að staðsetningu tækisins vegna símtals eða skilaboða í neyðarnúmer. Þetta getur gerst jafnvel þótt forritið sé ekki með staðsetningarheimild eða slökkt sé á staðsetningu tækisins. "<a href="https://support.google.com/android/answer/9319337">"Nánar"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Athugaðu símann"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Ekki náðist samband við símann þinn"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Reyna aftur?"</string> </resources> diff --git a/PermissionController/res/values-it-v33/strings.xml b/PermissionController/res/values-it-v33/strings.xml index 66187fa98..9bea693c9 100644 --- a/PermissionController/res/values-it-v33/strings.xml +++ b/PermissionController/res/values-it-v33/strings.xml @@ -29,7 +29,7 @@ <string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string> <string name="safety_center_more_issues_card_title" msgid="7425844746197493312">"Altri avvisi"</string> <string name="safety_center_dismissed_issues_card_title" msgid="2340129842725145733">"Avvisi chiusi"</string> - <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Espandi e visualizza un altro avviso}many{Espandi e visualizza altri # avvisi}other{Espandi e visualizza altri # avvisi}}"</string> + <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Espandi e visualizza un altro avviso}many{Espandi e visualizza altri # di avvisi}other{Espandi e visualizza altri # avvisi}}"</string> <string name="safety_center_issue_card_prefix_content_description" msgid="1447445289637043544">"Avviso. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>"</string> <string name="safety_center_resolved_issue_fallback" msgid="8548932070610766651">"Azione completata"</string> <string name="safety_center_qs_status_summary" msgid="5193925895830451177">"Controlla le impostazioni che possono proteggere meglio il tuo dispositivo"</string> diff --git a/PermissionController/res/values-it-v35/strings.xml b/PermissionController/res/values-it-v35/strings.xml new file mode 100644 index 000000000..61a64698f --- /dev/null +++ b/PermissionController/res/values-it-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sicurezza"</string> +</resources> diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml index 74467e320..d2b42e28b 100644 --- a/PermissionController/res/values-it/strings.xml +++ b/PermissionController/res/values-it/strings.xml @@ -17,10 +17,11 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="6098036489833144040">"App di controllo autorizzazioni"</string> - <string name="ok" msgid="1936281769725676272">"OK"</string> + <string name="ok" msgid="1936281769725676272">"Ok"</string> <string name="permission_search_keyword" msgid="1214451577494730543">"autorizzazioni"</string> <string name="cancel" msgid="8943320028373963831">"Annulla"</string> <string name="back" msgid="6249950659061523680">"Indietro"</string> + <string name="dialog_close" msgid="6840699812532384661">"Chiudi"</string> <string name="available" msgid="6007778121920339498">"Disponibile"</string> <string name="blocked" msgid="9195547604866033708">"Bloccato"</string> <string name="on" msgid="280241003226755921">"On"</string> @@ -70,7 +71,7 @@ <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Gestisci l\'accesso ai dati relativi a calendario, registri chiamate e altro"</string> <string name="granted_permission_decision" msgid="7824827491551861365">"Hai consentito l\'accesso dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g> a: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> <string name="denied_permission_decision" msgid="5308961501779563781">"Hai negato l\'accesso dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g> a: <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string> - <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Oggi}=1{1 giorno fa}many{# giorni fa}other{# giorni fa}}"</string> + <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Oggi}=1{1 giorno fa}many{# di giorni fa}other{# giorni fa}}"</string> <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disattiva app"</string> <string name="app_disable_dlg_text" msgid="3126943217146120240">"Se disattivi questa app, Android e altre app potrebbero non funzionare più come previsto. Tieni presente che non puoi eliminare questa app perché è preinstallata sul tuo dispositivo. Puoi scegliere di disattivare l\'app e nasconderla sul tuo dispositivo."</string> <string name="app_permission_manager" msgid="3903811137630909550">"Gestione autorizzazioni"</string> @@ -91,7 +92,7 @@ <string name="menu_show_24_hours_data" msgid="8228054833323380780">"Mostra ultime 24 ore"</string> <string name="manage_permission" msgid="2895385393037061964">"Gestisci autorizzazione"</string> <string name="no_apps" msgid="2412612731628386816">"Nessuna app"</string> - <string name="location_settings" msgid="3624412509133422562">"Geolocalizzazione"</string> + <string name="location_settings" msgid="3624412509133422562">"Impostazioni di localizzazione"</string> <string name="location_warning" msgid="2381649060929040962">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è un fornitore di servizi di geolocalizzazione per questo dispositivo. È possibile modificare l\'accesso alla posizione dalle impostazioni per la geolocalizzazione."</string> <string name="system_warning" msgid="1173400963234358816">"Se rifiuti questa autorizzazione, le funzionalità di base del dispositivo potrebbero non funzionare più come previsto."</string> <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Questa app è stata progettata per una versione precedente di Android. Se non consenti a questa app di accedere a foto e video, verrà negato anche l\'accesso a musica e altro audio."</string> @@ -139,15 +140,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="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_days" msgid="8238355545812998877">"{count,plural, =1{# giorno}many{# di giorni}other{# giorni}}"</string> + <string name="duration_used_hours" msgid="4983814806123370332">"{count,plural, =1{# ora}many{# di 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_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultimo giorno}many{Ultimi # giorni}other{Ultimi # giorni}}"</string> + <string name="permission_usage_last_n_days" msgid="7882626467375714145">"{count,plural, =1{Ultimo giorno}many{Ultimi # di 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="permission_usage_last_n_minutes" msgid="7817864229878281983">"{count,plural, =1{Ultimo minuto}many{Ultimi # di 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 +162,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_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_summary_not_used_in_past_n_days" msgid="4771868094611359651">"{count,plural, =1{Autorizzazione non utilizzata nell\'ultimo giorno}many{Autorizzazione non utilizzata in # di 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 in # di 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{Autorizzazione usata da # app}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> @@ -194,7 +195,7 @@ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Consenti accesso limitato"</string> <string name="precise_image_description" msgid="6349638632303619872">"Posizione esatta"</string> <string name="approximate_image_description" msgid="938803699637069884">"Posizione approssimativa"</string> - <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Posizione esatta"</string> + <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usa posizione esatta"</string> <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Quando la posizione esatta non è attiva, le app possono accedere alla tua posizione approssimativa"</string> <string name="app_permission_title" msgid="2090897901051370711">"Autorizzazione <xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header" msgid="2951363137032603806">"Accesso a <xliff:g id="PERM">%1$s</xliff:g> per questa app"</string> @@ -225,7 +226,7 @@ <string name="unused_apps_page_title" msgid="6986983535677572559">"App inutilizzate"</string> <string name="unused_apps_page_summary" msgid="1867593913217272155">"Se un\'app non viene usata per alcuni mesi:\n\n• Vengono rimosse le autorizzazioni per proteggere i tuoi dati\n• Vengono interrotte le notifiche per risparmiare batteria\n• Vengono rimossi i file temporanei per liberare spazio\n\nApri l\'app per consentire di nuovo autorizzazioni e notifiche."</string> <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Se un\'app non viene usata per un mese:\n\n• Vengono rimosse le autorizzazioni per proteggere i tuoi dati\n• Vengono rimossi i file temporanei per liberare spazio\n\nApri l\'app per consentire di nuovo le autorizzazioni."</string> - <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Aperte l\'ultima volta più di # mese fa}many{Aperte l\'ultima volta più di # mesi fa}other{Aperte l\'ultima volta più di # mesi fa}}"</string> + <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Aperte l\'ultima volta più di # mese fa}many{Aperte l\'ultima volta più di # di mesi fa}other{Aperte l\'ultima volta più di # mesi fa}}"</string> <string name="last_opened_summary" msgid="5248984030024968808">"Ultimo utilizzo dell\'app: <xliff:g id="DATE">%s</xliff:g>"</string> <string name="last_opened_summary_short" msgid="1646067226191176825">"Ultimo utilizzo: <xliff:g id="DATE">%s</xliff:g>"</string> <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"Se consenti la gestione di tutti i file, questa app potrà accedere, modificare ed eliminare tutti i file salvati in spazi di archiviazione comuni su questo dispositivo o su qualsiasi dispositivo di archiviazione connesso. L\'app potrà accedere ai file senza informarti."</string> @@ -251,24 +252,24 @@ <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Rifiutata/Accesso mai effettuato"</string> <string name="allowed_header" msgid="7769277978004790414">"Autorizzata"</string> <string name="allowed_always_header" msgid="6455903312589013545">"Autorizzazione sempre concessa"</string> - <string name="allowed_foreground_header" msgid="6845655788447833353">"Autorizzazione concessa solo durante l\'uso"</string> + <string name="allowed_foreground_header" msgid="6845655788447833353">"Autorizzazione concessa solo se in uso"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Autorizzate solo per contenuti multimediali"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Autorizzate per gestire tutti i file"</string> <string name="ask_header" msgid="2633816846459944376">"Chiedi ogni volta"</string> <string name="denied_header" msgid="903209608358177654">"Non autorizzata"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> su <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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="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="days" msgid="609563020985571393">"{count,plural, =1{1 giorno}many{# di giorni}other{# giorni}}"</string> + <string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ora}many{# di ore}other{# ore}}"</string> + <string name="minutes" msgid="4868414855445375753">"{count,plural, =1{# minuto}many{# di minuti}other{# minuti}}"</string> + <string name="seconds" msgid="5893958182059842734">"{count,plural, =1{# secondo}many{# di 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> <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Autorizzazioni rimosse per proteggere la tua privacy. Tocca per controllare"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Autorizzazioni rimosse per le app inutilizzate"</string> <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Alcune app non sono state usate per alcuni mesi. Tocca per controllare."</string> - <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app inutilizzata}many{# app inutilizzate}other{# app inutilizzate}}"</string> + <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# app inutilizzata}many{# di app inutilizzate}other{# app inutilizzate}}"</string> <string name="unused_apps_notification_content" msgid="9195026773244581246">"Le autorizzazioni e i file temporanei sono stati rimossi; le notifiche sono state interrotte. Tocca per controllare."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Controlla le app di cui sono state rimosse le autorizzazioni"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"Per le app che non usi da un po\' di tempo sono stati rimossi i file temporanei e le autorizzazioni e sono state interrotte le notifiche."</string> @@ -277,7 +278,7 @@ <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Durante la guida, hai consentito all\'app <xliff:g id="APP">%1$s</xliff:g> di accedere a <xliff:g id="PERMISSION">%2$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Durante la guida, hai consentito all\'app <xliff:g id="APP">%1$s</xliff:g> di accedere a <xliff:g id="PERMISSION_1">%2$s</xliff:g> e <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"Durante la guida, hai concesso <xliff:g id="COUNT">%1$d</xliff:g> autorizzazioni all\'app <xliff:g id="APP">%2$s</xliff:g>"</string> - <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_0">%1$s</xliff:g> e a # altra app}many{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}other{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}}"</string> + <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_0">%1$s</xliff:g> e a # altra app}many{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e a # di altre app}other{Durante la guida, hai consentito l\'accesso a <xliff:g id="APP_1">%1$s</xliff:g> e ad altre # app}}"</string> <string name="go_to_settings" msgid="1053735612211228335">"Vai a Impostazioni"</string> <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Alcune app non sono state usate per alcuni mesi"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"Autorizzazioni rimosse"</string> @@ -421,7 +422,7 @@ <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"La videocamera e il microfono sono in uso per un servizio di sistema"</string> <string name="system_uses_camera" msgid="1911223105234441470">"La videocamera è in uso per un servizio di sistema"</string> <string name="other_use" msgid="6564855051022776692">"Altro uso:"</string> - <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"OK"</string> + <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"Ok"</string> <string name="ongoing_usage_dialog_title" msgid="683836493556628569">"Uso recente di <xliff:g id="TYPES_LIST">%s</xliff:g>"</string> <string name="ongoing_usage_dialog_title_mic" msgid="5966714811125593992">"Utilizzo recente di Microfono"</string> <string name="ongoing_usage_dialog_title_camera" msgid="7819329688650711470">"Utilizzo recente di Fotocamera"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nessuna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Predefinita)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nessuna app"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Altri servizi NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selezionata"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selezionata - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"accesso speciale per le app"</string> @@ -660,7 +662,7 @@ <string name="learn_about_data_sharing" msgid="4200480587079488045">"Scopri di più sulla condivisione dei dati"</string> <string name="shares_location_with_third_parties" msgid="2278051743742057767">"Ora i tuoi dati sulla posizione vengono condivisi con terze parti"</string> <string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Ora i tuoi dati sulla posizione vengono condivisi con terze parti per scopi pubblicitari o di marketing"</string> - <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Aggiornamenti nelle ultime 24 ore}=1{Aggiornamenti nelle ultime 24 ore}many{Aggiornamenti negli ultimi # giorni}other{Aggiornamenti negli ultimi # giorni}}"</string> + <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{App aggiornate nelle ultime 24 ore}=1{App aggiornate nelle ultime 24 ore}many{App aggiornate negli ultimi # di giorni}other{App aggiornate negli ultimi # giorni}}"</string> <string name="no_updates_at_this_time" msgid="9031085635689982935">"Nessun aggiornamento al momento"</string> <string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Aggiornamenti relativi alla condivisione dei dati"</string> <string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Alcune app hanno cambiato la modalità di condivisione dei tuoi dati sulla posizione"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Ultimo accesso ieri: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ultimo accesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"La password monouso è: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Consenti impostazioni con limitazioni"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Impostazione con limitazioni"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per la tua sicurezza, questa impostazione non è al momento disponibile."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Azione non disponibile durante una telefonata"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"È vietato consentire alle app di installare altre app durante una telefonata.\n\n I truffatori richiedono spesso questo tipo di azione durante le conversazioni telefoniche, quindi è bloccata per proteggerti. Se qualcuno che non conosci ti guida a compiere questa azione, potrebbe trattarsi di una truffa."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"All\'app è stato negato l\'accesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'app ha richiesto l\'accesso a un\'autorizzazione sensibile che può mettere a rischio le tue informazioni finanziarie e personali.<xliff:g id="ID_1"><br><br></xliff:g>È possibile che l\'app non funzioni correttamente senza questa autorizzazione limitata. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Scopri di più su come consentire l\'accesso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"All\'app è stato negato l\'accesso al ruolo <xliff:g id="ROLE_NAME">%1$s</xliff:g> predefinita"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ok"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Richiesta di autorizzazione rifiutata"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Questa app richiede autorizzazioni aggiuntive, ma non è possibile concedere autorizzazioni in una sessione di streaming. Devi prima concedere l\'autorizzazione sul tuo smartphone."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Per messaggio o chiamata di emergenza"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Posizione inviata ai servizi di emergenza"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Questa app ha eseguito l\'accesso alla posizione del tuo dispositivo durante una chiamata o l\'invio di un messaggio a un numero di emergenza. Questo può accadere anche quando l\'app non ha l\'autorizzazione di accesso alla posizione o se la localizzazione del dispositivo è disattivata. "<a href="https://support.google.com/android/answer/9319337">"Scopri di più"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Controlla il tuo smartphone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Impossibile collegarsi allo smartphone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vuoi riprovare?"</string> </resources> diff --git a/PermissionController/res/values-iw-v35/strings.xml b/PermissionController/res/values-iw-v35/strings.xml new file mode 100644 index 000000000..b9ebf0891 --- /dev/null +++ b/PermissionController/res/values-iw-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"אבטחה"</string> +</resources> diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml index 5685bef70..be7a19ca0 100644 --- a/PermissionController/res/values-iw/strings.xml +++ b/PermissionController/res/values-iw/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"הרשאות"</string> <string name="cancel" msgid="8943320028373963831">"ביטול"</string> <string name="back" msgid="6249950659061523680">"חזרה"</string> + <string name="dialog_close" msgid="6840699812532384661">"סגירה"</string> <string name="available" msgid="6007778121920339498">"הגישה מותרת"</string> <string name="blocked" msgid="9195547604866033708">"הגישה חסומה"</string> <string name="on" msgid="280241003226755921">"מופעל"</string> @@ -55,7 +56,7 @@ <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"לשנות למיקום מדויק"</string> <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"להמשיך עם מיקום משוער"</string> <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"רק הפעם"</string> - <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"להתיר כל הזמן"</string> + <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"כן, כל הזמן"</string> <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"אישור לניהול כל הקבצים"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"אישור גישה לקבצי מדיה"</string> <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"אפליקציות"</string> @@ -109,7 +110,7 @@ <!-- no translation found for background_access_chooser_dialog_choices:0 (1351721623256561996) --> <!-- no translation found for background_access_chooser_dialog_choices:1 (9127301153688725448) --> <!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) --> - <string name="permission_access_always" msgid="1474641821883823446">"להתיר כל הזמן"</string> + <string name="permission_access_always" msgid="1474641821883823446">"כן, כל הזמן"</string> <string name="permission_access_only_foreground" msgid="7801170728159326195">"רק כשהאפליקציה בשימוש"</string> <string name="permission_access_never" msgid="4647014230217936900">"אין אישור"</string> <string name="loading" msgid="4789365003890741082">"בטעינה…"</string> @@ -200,15 +201,15 @@ <string name="app_permission_header" msgid="2951363137032603806">"לאפליקציה תהיה גישה ל<xliff:g id="PERM">%1$s</xliff:g>"</string> <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"גישה ל<xliff:g id="PERM">%1$s</xliff:g> לאפליקציה הזו ב<xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"הצגת כל ההרשאות של \'<xliff:g id="APP">%1$s</xliff:g>\'"</string> - <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"הצגת כל האפליקציות עם ההרשאה הזו"</string> + <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"אני רוצה לראות את כל האפליקציות עם ההרשאה הזו"</string> <string name="assistant_mic_label" msgid="1011432357152323896">"השימוש במיקרופון של Assistant"</string> <string name="unused_apps_category_title" msgid="2988455616845243901">"הגדרות של אפליקציות שמזמן לא השתמשת בהן"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"הסרת ההרשאות כשלא בשימוש"</string> <string name="unused_apps_label" msgid="2595428768404901064">"הסרת הרשאות ופינוי מקום אחסון"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"השהיית הפעילות באפליקציה אם אין בה שימוש"</string> - <string name="unused_apps_label_v3" msgid="693340578642156657">"ניהול האפליקציה כשהיא לא בשימוש"</string> + <string name="unused_apps_label_v3" msgid="693340578642156657">"הגבלת האפליקציה כשהיא לא בשימוש"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"הסרת ההרשאות, מחיקה של הקבצים הזמניים, הפסקה של קבלת ההתראות"</string> - <string name="unused_apps_summary_v2" msgid="5011313200815115802">"הסרת ההרשאות, מחיקה של הקבצים הזמניים, הפסקה של קבלת ההתראות והעברת האפליקציה לארכיון"</string> + <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ההרשאות יוסרו, הקבצים הזמניים יימחקו, ההתראות יופסקו והאפליקציה תעבור לארכיון"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"כדי להגן על הנתונים שלך, אם האפליקציה הזו לא תהיה בשימוש במשך מספר חודשים, ההרשאות שניתנו לה יוסרו."</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"כדי להגן על הנתונים שלך, אם האפליקציה לא תהיה בשימוש במשך מספר חודשים, ההרשאות הבאות יוסרו: <xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"כדי להגן על הנתונים שלך, הוסרו הרשאות מאפליקציות שלא השתמשת בהן במשך מספר חודשים."</string> @@ -265,11 +266,11 @@ <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> - <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"הוסרו הרשאות כדי להגן על הפרטיות שלך. יש להקיש כדי לבדוק"</string> + <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"הוסרו הרשאות כדי להגן על הפרטיות שלך. יש ללחוץ כדי לבדוק"</string> <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"הוסרו הרשאות לאפליקציות שמזמן לא השתמשת בהן"</string> - <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"יש אפליקציות שלא נעשה בהן שימוש כבר כמה חודשים. אפשר להקיש כדי לבדוק."</string> + <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"יש אפליקציות שלא נעשה בהן שימוש כבר כמה חודשים. אפשר ללחוץ כדי לבדוק."</string> <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{אפליקציה אחת שמזמן לא השתמשת בה}one{# אפליקציות שמזמן לא השתמשת בהן}two{# אפליקציות שמזמן לא השתמשת בהן}other{# אפליקציות שמזמן לא השתמשת בהן}}"</string> - <string name="unused_apps_notification_content" msgid="9195026773244581246">"ההרשאות בוטלו, הקבצים הזמניים הוסרו וההתראות הופסקו. יש להקיש כדי לבדוק."</string> + <string name="unused_apps_notification_content" msgid="9195026773244581246">"ההרשאות בוטלו, הקבצים הזמניים הוסרו וההתראות הופסקו. יש ללחוץ כדי לבדוק."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"בדיקת אפליקציות שההרשאות שלהן הוסרו"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"ההרשאות והקבצים הזמניים של אפליקציות שלא השתמשת בהן זמן מה הוסרו, וההתראות מהן הופסקו."</string> <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"בדיקת האפליקציות"</string> @@ -286,7 +287,7 @@ <string name="months_ago" msgid="1766026492610646354">"לפני <xliff:g id="COUNT">%1$d</xliff:g> חודשים"</string> <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"הוסרו הרשאות כדי להגן על הפרטיות שלך"</string> <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"האפליקציה <xliff:g id="APP_NAME">%s</xliff:g> קיבלה גישה ברקע למיקום שלך"</string> - <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"האפליקציה הזו יכולה תמיד לגשת למיקום שלך. יש להקיש כדי לשנות את ההגדרה הזו."</string> + <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"האפליקציה הזו יכולה תמיד לגשת למיקום שלך. יש ללחוץ כדי לשנות את ההגדרה הזו."</string> <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"בדיקת אפליקציה עם גישה להתראות"</string> <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"האפליקציה <xliff:g id="APP_NAME">%s</xliff:g> יכולה לסגור את ההתראות שלך, לפעול לפיהן ולגשת לתוכן בהתראות"</string> <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"האפליקציה הזו יכולה לסגור את ההתראות שלך, לפעול לפיהן ולגשת לתוכן בהתראות. לחלק מהאפליקציות נדרשת גישה כזו כדי לתפקד כמו שצריך."</string> @@ -301,12 +302,12 @@ <string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"הגישה הוסרה"</string> <string name="safety_center_notification_app_label" msgid="2457720616141926534">"מערכת Android"</string> <string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"הוסרו הרשאות הניתנות לאפליקציה כדי להגן על הפרטיות"</string> - <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%s</xliff:g>. יש להקיש כדי לבדוק."</string> - <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%s</xliff:g> ובאפליקציה אחת נוספת. יש להקיש כדי לבדוק."</string> - <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> וב-<xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> אפליקציות נוספות. יש להקיש כדי לבדוק."</string> + <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%s</xliff:g>. יש ללחוץ כדי לבדוק."</string> + <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%s</xliff:g> ובאפליקציה אחת נוספת. יש ללחוץ כדי לבדוק."</string> + <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"כבר כמה חודשים לא נעשה שימוש באפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> וב-<xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> אפליקציות נוספות. יש ללחוץ כדי לבדוק."</string> <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"אפליקציה אחת לא נמצאת בשימוש"</string> <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> אפליקציות לא נמצאות בשימוש"</string> - <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"הרשאות יוסרו כדי להגן על הפרטיות שלך. יש להקיש כדי לבדוק."</string> + <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"הרשאות יוסרו כדי להגן על הפרטיות שלך. יש ללחוץ כדי לבדוק."</string> <string name="unused_apps_title" msgid="8589298917717872239">"אפליקציות שמזמן לא השתמשת בהן"</string> <string name="unused_apps_subtitle_after" msgid="2034267519506357898">"הרשאות הוסרו מהאפליקציות"</string> <string name="unused_apps_subtitle_before" msgid="5233302577076132427">"הרשאות יוסרו מהאפליקציות"</string> @@ -345,7 +346,7 @@ <string name="no_apps_allowed" msgid="7718822655254468631">"אין אפליקציות שקיבלו הרשאה"</string> <string name="no_apps_allowed_full" msgid="8011716991498934104">"לאף אפליקציה אין הרשאה לכל הקבצים"</string> <string name="no_apps_allowed_scoped" msgid="4908850477787659501">"לאף אפליקציה אין הרשאה למדיה בלבד"</string> - <string name="no_apps_denied" msgid="7663435886986784743">"אין אפליקציות שלא קיבלו ההרשאה"</string> + <string name="no_apps_denied" msgid="7663435886986784743">"אין אפליקציות שלא קיבלו הרשאה"</string> <string name="car_permission_selected" msgid="180837028920791596">"נבחר"</string> <string name="settings" msgid="5409109923158713323">"הגדרות"</string> <string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"לשירות <xliff:g id="SERVICE_NAME">%s</xliff:g> יש גישה מלאה למכשיר שלך"</string> @@ -357,7 +358,7 @@ <string name="role_assistant_description" msgid="6622458130459922952">"אפליקציות עזרה יכולות לסייע על סמך המידע שמוצג לך במסך. אפליקציות מסוימות תומכות גם בשירותי מרכז אפליקציות וגם בקלט קולי כדי לספק סיוע משולב."</string> <string name="role_browser_label" msgid="2877796144554070207">"אפליקציית ברירת מחדל לדפדפן"</string> <string name="role_browser_short_label" msgid="6745009127123292296">"אפליקציית דפדפן"</string> - <string name="role_browser_description" msgid="3465253637499842671">"אפליקציות שמספקות לך גישה לאינטרנט ומציגות קישורים להקשה"</string> + <string name="role_browser_description" msgid="3465253637499842671">"אפליקציות שמספקות לך גישה לאינטרנט ומציגות קישורים ללחיצה"</string> <string name="role_browser_request_title" msgid="2895200507835937192">"להגדיר את <xliff:g id="APP_NAME">%1$s</xliff:g> כאפליקציית הדפדפן המשמשת כברירת מחדל?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"אין צורך בהרשאות"</string> <string name="role_dialer_label" msgid="1100224146343237968">"אפליקציית ברירת המחדל לטלפון"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ללא"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ברירת מחדל של המערכת)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"אין אפליקציות"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"שירותי NFC אחרים"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"נבחרה"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"נבחרה – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"גישה מיוחדת של אפליקציה"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת להעלות נתונים של ניפוי באגים."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"לשתף נתונים של ניפוי באגים?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"המערכת איתרה בעיה."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"אפליקציית <xliff:g id="APP_NAME_0">%1$s</xliff:g> מבקשת להעלות דוח על באגים מהמכשיר הזה, שנוצר בתאריך <xliff:g id="DATE">%2$s</xliff:g> בשעה <xliff:g id="TIME">%3$s</xliff:g>. דוחות על באגים כוללים מידע אישי על המכשיר או מידע אישי שנשמר באמצעות אפליקציות. למשל, שמות משתמשים, נתוני מיקום, מזהי מכשיר ופרטי רשת. יש לשתף דוחות על באגים רק עם אפליקציות ואנשים מהימנים. לאפשר לאפליקציית <xliff:g id="APP_NAME_1">%4$s</xliff:g> להעלות דוח על באגים?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> מבקשת להעלות דוח על באגים מהמכשיר הזה, שנוצר בתאריך <xliff:g id="DATE">%2$s</xliff:g> בשעה <xliff:g id="TIME">%3$s</xliff:g>. דוחות על באגים כוללים מידע אישי על המכשיר או מידע אישי שנשמר באמצעות אפליקציות. למשל, שמות משתמשים, נתוני מיקום, מזהי מכשיר ופרטי רשת. יש לשתף דוחות על באגים רק עם אפליקציות ואנשים שאמינים בעיניך. לאפשר לאפליקציית <xliff:g id="APP_NAME_1">%4$s</xliff:g> להעלות דוח על באגים?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"אירעה שגיאה בעיבוד הדוח על הבאג עבור <xliff:g id="APP_NAME">%1$s</xliff:g>. לכן, נדחה שיתוף הנתונים המפורטים לגבי ניפוי השגיאות. מצטערים על ההפרעה."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"כן, זה בסדר"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"אני לא מרשה"</string> @@ -469,7 +471,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה לתמונות ולמדיה במכשיר <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה לאנשי הקשר שלך?"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה לאנשי הקשר במכשיר <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למיקום המכשיר?"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"לתת לאפליקציה \'<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>\' הרשאת גישה למיקום המכשיר?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למיקום של <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"לאפליקציה תהיה גישה אל נתוני המיקום רק בזמן השימוש בה"</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למיקום המכשיר?"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"גישה אתמול: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"תאריך גישה: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ב-<xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"הסיסמה החד-פעמית שלך היא 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"הרשאה להגדרות מוגבלות"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"הגדרה מוגבלת"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"מטעמי אבטחה, ההגדרה הזו לא זמינה כרגע."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"הפעולה לא זמינה במהלך שיחת טלפון"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"אי אפשר לתת לאפליקציות הרשאה להתקין אפליקציות אחרות במהלך שיחת טלפון.\n\n לרוב, אנשים שמבצעים תרמיות מבקשים לבצע פעולות מהסוג הזה במהלך שיחות טלפון, לכן הפעולה הזו חסומה כדי להגן עליך. אם מישהו לא מוכר מנחה אותך לבצע את הפעולה הזו, יכול להיות שמדובר בתרמית."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"בקשת הגישה של האפליקציה ל<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> נדחתה"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"האפליקציה ביקשה הרשאת גישה למידע רגיש שעלולה לסכן את המידע האישי והפיננסי שלך.<xliff:g id="ID_1"><br><br></xliff:g>יכול להיות שהאפליקציה לא תעבוד כמו שצריך ללא ההרשאה המוגבלת הזו. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>מידע נוסף על מתן גישה להרשאות</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"בקשת הגישה של האפליקציה לשמש כברירת המחדל של <xliff:g id="ROLE_NAME">%1$s</xliff:g> נדחתה"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"הבנתי"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"בקשת ההרשאה בוטלה"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"האפליקציה הזו מבקשת הרשאות נוספות, אבל אי אפשר לתת הרשאות בזמן פעילות של סטרימינג. צריך לתת את ההרשאה קודם בטלפון."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"לשיחת חירום או שליחת הודעת טקסט במקרה חירום"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"המיקום נשלח לשירותי החירום"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"האפליקציה הזו ניגשה למיקום המכשיר שלך בזמן שיחה או שליחת הודעת טקסט למספר חירום. זה יכול לקרות גם אם לאפליקציה אין הרשאת מיקום או שמיקום המכשיר מושבת. "<a href="https://support.google.com/android/answer/9319337">"מידע נוסף"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"צריך לבדוק בטלפון"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"אי אפשר להתחבר לטלפון"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"לנסות שוב?"</string> </resources> diff --git a/PermissionController/res/values-ja-v35/strings.xml b/PermissionController/res/values-ja-v35/strings.xml new file mode 100644 index 000000000..301dec443 --- /dev/null +++ b/PermissionController/res/values-ja-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"セキュリティ"</string> +</resources> diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml index 946365155..99c927b67 100644 --- a/PermissionController/res/values-ja/strings.xml +++ b/PermissionController/res/values-ja/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"権限"</string> <string name="cancel" msgid="8943320028373963831">"キャンセル"</string> <string name="back" msgid="6249950659061523680">"戻る"</string> + <string name="dialog_close" msgid="6840699812532384661">"閉じる"</string> <string name="available" msgid="6007778121920339498">"使用可能"</string> <string name="blocked" msgid="9195547604866033708">"ブロック中"</string> <string name="on" msgid="280241003226755921">"ON"</string> @@ -73,7 +74,7 @@ <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{今日}=1{1 日前}other{# 日前}}"</string> <string name="app_disable_dlg_positive" msgid="7418444149981904940">"アプリを無効にする"</string> <string name="app_disable_dlg_text" msgid="3126943217146120240">"このアプリを無効にすると、Android などの他のアプリが正しく動作しなくなるおそれがあります。このアプリはデバイスにプリインストールされているため、削除できません。無効にするには、このアプリをオフにし、デバイスにアプリが表示されないようにします。"</string> - <string name="app_permission_manager" msgid="3903811137630909550">"権限マネージャ"</string> + <string name="app_permission_manager" msgid="3903811137630909550">"権限マネージャー"</string> <string name="never_ask_again" msgid="4728762438198560329">"今後表示しない"</string> <string name="no_permissions" msgid="3881676756371148563">"権限がありません"</string> <string name="additional_permissions" msgid="5801285469338873430">"その他の権限"</string> @@ -184,8 +185,8 @@ <string name="app_permission_usage_summary" msgid="390383661936709672">"アクセス: <xliff:g id="NUM">%1$s</xliff:g> 回。合計時間: <xliff:g id="DURATION">%2$s</xliff:g>。最終使用: <xliff:g id="TIME">%3$s</xliff:g>前。"</string> <string name="app_permission_usage_summary_no_duration" msgid="3698475875179457400">"アクセス: <xliff:g id="NUM">%1$s</xliff:g> 回。最終使用: <xliff:g id="TIME">%2$s</xliff:g>前。"</string> <string name="app_permission_button_allow" msgid="5808039516494774647">"許可する"</string> - <string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"すべてのファイルの管理を許可"</string> - <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"メディアへのアクセスのみを許可"</string> + <string name="app_permission_button_allow_all_files" msgid="1792232272599018825">"すべての管理を許可"</string> + <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_always_allow_all" msgid="4905699259378428855">"常にすべて許可"</string> @@ -210,7 +211,7 @@ <string name="unused_apps_summary" msgid="8839466950318403115">"権限と一時ファイルを削除し、通知を停止します"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"権限と一時ファイルを削除し、通知を停止し、アプリをアーカイブします"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"データ保護のため、このアプリが数か月使用されていない場合はアプリの権限が取り消されます。"</string> - <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"データ保護のため、アプリが数か月使用されていない場合は以下の権限が取り消されます。<xliff:g id="PERMS">%1$s</xliff:g>"</string> + <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"データ保護のため、アプリが数か月使用されていない場合は次の権限が取り消されます。<xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"データ保護のため、数か月使用していないアプリの権限を削除しました。"</string> <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"もう一度権限を許可するには、アプリを開いてください。"</string> <string name="auto_revoke_disabled" msgid="8697684442991567188">"このアプリでは現在、自動削除は無効になっています。"</string> @@ -252,7 +253,7 @@ <string name="allowed_header" msgid="7769277978004790414">"許可"</string> <string name="allowed_always_header" msgid="6455903312589013545">"常に許可"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"使用中のみ許可"</string> - <string name="allowed_storage_scoped" msgid="5383645873719086975">"メディアへのアクセスのみが許可されたアプリ"</string> + <string name="allowed_storage_scoped" msgid="5383645873719086975">"アクセスのみ許可"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"すべてのファイルの管理が許可されたアプリ"</string> <string name="ask_header" msgid="2633816846459944376">"毎回確認する"</string> <string name="denied_header" msgid="903209608358177654">"許可しない"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"なし"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(システムのデフォルト)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"アプリなし"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"その他の NFC サービス"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"選択済み"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"選択済み - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"特別なアプリアクセス"</string> @@ -550,9 +552,7 @@ <string name="privdash_label_none" msgid="5991866260360484858">"なし"</string> <string name="privdash_label_24h" msgid="1512532123865375319">"過去 \n24 時間"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"過去\n7 日間"</string> - <!-- String.format failed for translation --> - <!-- no translation found for privdash_usage_percent (6893824766124414127) --> - <skip /> + <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g>%%"</string> <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> は Android によって保護されています。あなたのデータはこのデバイス上で処理されるため、このアプリの権限の使用状況はステータスバーやプライバシー ダッシュボードには表示されません。"</string> <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> は Android によって保護されています。あなたのデータはこのデバイス上で処理されるため、このアプリの権限の使用状況はプライバシー ダッシュボードには表示されません。"</string> <string name="blocked_camera_title" msgid="1128510551791284384">"デバイスのカメラがブロックされています"</string> @@ -671,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"昨日の <xliff:g id="TIME_DATE">%1$s</xliff:g> にアクセス"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> にアクセス"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ワンタイム パスワードは 132435 です"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"制限付き設定を許可"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"制限付き設定"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"セキュリティ保護のため、この設定は現在利用できません。"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"この操作は通話中はできません"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話中にアプリが他のアプリをインストールすることはできません。\n\n このような操作は、詐欺師が電話での会話中に要求することが多いため、ユーザー保護を目的に禁止されています。知らない相手からこのような操作を求められた場合は、詐欺の可能性があります。"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"アプリは<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>へのアクセスを拒否されました"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"機密情報に関わる権限へのアクセスをアプリがリクエストしました。この権限へのアクセスは、あなたの個人情報や財務情報を危険にさらす恐れがあります。<xliff:g id="ID_1"><br><br></xliff:g>この制限付きの権限がないとアプリは正しく動作しない可能性があります。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>アクセスを許可する方法の詳細</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"アプリはデフォルトの<xliff:g id="ROLE_NAME">%1$s</xliff:g>としてのアクセスを拒否されました"</string> @@ -683,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"権限のリクエストが抑制されています"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"このアプリは追加の権限を求めていますが、ストリーミング セッションでは権限を付与できません。スマートフォンで先に権限を付与してください。"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"緊急通報(電話またはテキスト メッセージ)の場合"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"緊急サービスへの位置情報の送信"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"このアプリは緊急通報番号に電話またはテキスト メッセージで通報する際にデバイスの位置情報にアクセスしました。このアクセスは、アプリに位置情報の利用許可が付与されていない場合やデバイスの位置情報が OFF の場合も発生することがあります。"<a href="https://support.google.com/android/answer/9319337">"詳細"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"スマートフォンを確認してください"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"スマートフォンに接続できません"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"再試行しますか?"</string> </resources> diff --git a/PermissionController/res/values-ka-v35/strings.xml b/PermissionController/res/values-ka-v35/strings.xml new file mode 100644 index 000000000..fb932751b --- /dev/null +++ b/PermissionController/res/values-ka-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"უსაფრთხოება"</string> +</resources> diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml index 5bfebc26d..ec1b1bb3a 100644 --- a/PermissionController/res/values-ka/strings.xml +++ b/PermissionController/res/values-ka/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ნებართვები"</string> <string name="cancel" msgid="8943320028373963831">"გაუქმება"</string> <string name="back" msgid="6249950659061523680">"უკან"</string> + <string name="dialog_close" msgid="6840699812532384661">"დახურვა"</string> <string name="available" msgid="6007778121920339498">"ხელმისაწვდომი"</string> <string name="blocked" msgid="9195547604866033708">"დაბლოკილი"</string> <string name="on" msgid="280241003226755921">"ჩართვა"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"არცერთი"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(სისტემის ნაგულისხმევი)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"აპები არ არის"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"სხვა NFC სერვისები"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"არჩეული"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"არჩეული - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"აპების სპეციალური წვდომა"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"წვდომა განხორციელდა გუშინ: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"წვდომა განხორციელდა: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"თქვენი ერთჯერადი პაროლია: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"შეზღუდული პარამეტრების დაშვება"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"შეზღუდული პარამეტრი"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"თქვენივე უსაფრთხოებისთვის ეს პარამეტრი ამჟამად მიუწვდომელია."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"სატელეფონო ზარისას მოქმედება არ არის ხელმისაწვდომი"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"აპებისთვის სხვა აპების ინსტალაციის დაშვება არ არის ნებადართული ტელეფონზე ზარის დროს.\n\n თაღლითები ხშირად ითხოვენ ამ ტიპის ქმედებას ტელეფონზე საუბრისას, შესაბამისად, აღნიშნული ქმედება დაიბლოკა თქვენი უსაფრთხოებისთვის. თუ ვინმემ, ვისაც არ იცნობთ, მიგითითათ ამ ქმედების განხორციელებისკენ, ეს შესაძლოა იყოს თაღლითური სქემა."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"აპისთვის უარყოფილია ნებართვაზე წვდომა: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"აპმა სენსიტიურ ინფორმაციაზე წვდომა მოითხოვა, რაც თქვენს პირად და ფინანსურ ინფორმაციას საფრთხის ქვეშ აყენებს.<xliff:g id="ID_1"><br><br></xliff:g>შესაძლოა აპმა ამ შეზღუდული ნებართვის გარეშე სათანადოდ ვერ იმუშაოს. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>შეიტყვეთ მეტი ნებართვის დაშვების შესახებ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"აპისთვის ნაგულისხმევ როლზე (<xliff:g id="ROLE_NAME">%1$s</xliff:g>) წვდომა უარყოფილია"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"კარგი"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ნებართვის მოთხოვნა შეჩერებულია"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ეს აპი დამატებით ნებართვებს მოითხოვს, მაგრამ ნებართვების მიცემა შეუძლებელია სტრიმინგის სესიაში. თავდაპირველად მიანიჭეთ ნებართვა ტელეფონზე."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"გადაუდებელი ზარის ან ტექსტური შეტყობინებისთვის"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"გადაუდებელი დახმარების სამსახურებთან მდებარეობის გაგზავნა"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ამ აპმა წვდომა მიიღო თქვენი მოწყობილობის მდებარეობაზე ზარის ან ტექსტური შეტყობინების გაგზავნისას გადაუდებელი დახმარების სამსახურის ნომერზე. ეს შეიძლება მოხდეს მაშინაც კი, როდესაც აპს არ აქვს მდებარეობის ნებართვა ან მოწყობილობის მდებარეობა გამორთულია. "<a href="https://support.google.com/android/answer/9319337">"შეიტყვეთ მეტი"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"შეამოწმეთ თქვენი ტელეფონი"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"თქვენს ტელეფონთან დაკავშირება ვერ მოხერხდა"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"გსურთ ხელახლა ცდა?"</string> </resources> diff --git a/PermissionController/res/values-kk-v35/strings.xml b/PermissionController/res/values-kk-v35/strings.xml new file mode 100644 index 000000000..6c5e61d14 --- /dev/null +++ b/PermissionController/res/values-kk-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Қауіпсіздік"</string> +</resources> diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml index 057eb51e0..a5680c1e5 100644 --- a/PermissionController/res/values-kk/strings.xml +++ b/PermissionController/res/values-kk/strings.xml @@ -21,8 +21,9 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"рұқсаттар"</string> <string name="cancel" msgid="8943320028373963831">"Бас тарту"</string> <string name="back" msgid="6249950659061523680">"Артқа"</string> + <string name="dialog_close" msgid="6840699812532384661">"Жабу"</string> <string name="available" msgid="6007778121920339498">"Қолжетімді"</string> - <string name="blocked" msgid="9195547604866033708">"Бөгелген"</string> + <string name="blocked" msgid="9195547604866033708">"Блокталған"</string> <string name="on" msgid="280241003226755921">"Қосулы"</string> <string name="off" msgid="1438489226422866263">"Өшіру"</string> <string name="uninstall_or_disable" msgid="4496612999740858933">"Жою немесе өшіру"</string> @@ -250,7 +251,7 @@ <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Ешқашан пайдаланбады"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Тыйым салынған/Ешқашан пайдаланбаған"</string> <string name="allowed_header" msgid="7769277978004790414">"Рұқсат берілгендер"</string> - <string name="allowed_always_header" msgid="6455903312589013545">"Біржола рұқсат берілгендер"</string> + <string name="allowed_always_header" msgid="6455903312589013545">"Біржола рұқсат берілген"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"Пайдаланғанда ғана рұқсат берілгендер"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Тек мультимедианы пайдалана алатын қолданбалар"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Барлық файлдарды басқара алатын қолданбалар"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Жоқ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(жүйенің әдепкі қолданбасы)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Қолданба жоқ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Басқа NFC қызметтері"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Таңдалды"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Таңдалды – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"арнайы қолданба рұқсаты"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Кеше пайдаланылған уақыты: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Пайдаланылған уақыты: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Бір реттік құпия сөзіңіз: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Шектелген параметрлерге рұқсат беру"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Шектелген параметр"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Қауіпсіздік мақсатында бұл параметрді қазір пайдалану мүмкін емес."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Телефон қоңырауы кезінде бұл әрекет мүмкін емес"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Телефон қоңырауы кезінде қолданбаларға басқа қолданбаларды орнатуға тыйым салынады.\n\n Алаяқтар телефонмен сөйлесу кезінде көбіне осындай әрекеттерді жасауды сұрайды, сондықтан сізді қорғау үшін бұл әрекет блокталған. Таныс емес адам осылай әрекет етуге нұсқау берсе, бұл алаяқтық болуы мүмкін."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Қолданбаға <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> рұқсаты берілмеді"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Қолданба жеке және қаржылық ақпаратыңызға қауіп төндіруі мүмкін құпия ақпарат рұқсатын сұрады.<xliff:g id="ID_1"><br><br></xliff:g>Қолданба бұл шектеулі рұқсатсыз дұрыс жұмыс істемеуі мүмкін. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Рұқсат беру туралы ақпарат</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Қолданбаға әдепкі <xliff:g id="ROLE_NAME">%1$s</xliff:g> болу рұқсаты берілмеді"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Жарайды"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Рұқсат сұрауы блокталды"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Бұл қолданба қосымша рұқсаттар сұрап жатыр, бірақ трансляция жүріп жатқанда рұқсат беру мүмкін емес. Алдымен телефонда рұқсат беріңіз."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Құтқару қызметіне қоңырау шалуға немесе мәтіндік хабар жіберуге арналған"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Локация құтқару қызметтеріне жіберілді"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Бұл қолданба құтқару қызметінің нөміріне қоңырау шалу немесе мәтіндік хабар жіберу кезінде құрылғының локациясын пайдаланды. Локацияны пайдалану рұқсаты болмаған немесе құрылғы локациясы өшірулі болған жағдайда да, қолданба локацияны пайдалануы мүмкін. "<a href="https://support.google.com/android/answer/9319337">"Толық ақпарат"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Телефоныңызды тексеріңіз"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Телефоныңызға қосылу мүмкін болмады."</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Қайталау керек пе?"</string> </resources> diff --git a/PermissionController/res/values-km-v35/strings.xml b/PermissionController/res/values-km-v35/strings.xml new file mode 100644 index 000000000..e461c54d5 --- /dev/null +++ b/PermissionController/res/values-km-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"សន្តិសុខ"</string> +</resources> diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml index f54ef1631..3712f99ac 100644 --- a/PermissionController/res/values-km/strings.xml +++ b/PermissionController/res/values-km/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ការអនុញ្ញាត"</string> <string name="cancel" msgid="8943320028373963831">"បោះបង់"</string> <string name="back" msgid="6249950659061523680">"ថយក្រោយ"</string> + <string name="dialog_close" msgid="6840699812532384661">"បិទ"</string> <string name="available" msgid="6007778121920339498">"អាចចូលប្រើបាន"</string> <string name="blocked" msgid="9195547604866033708">"បានទប់ស្កាត់"</string> <string name="on" msgid="280241003226755921">"បើក"</string> @@ -79,7 +80,7 @@ <string name="additional_permissions" msgid="5801285469338873430">"ការអនុញ្ញាតបន្ថែម"</string> <string name="app_permissions_info_button_label" msgid="7633312050729974623">"បើកព័ត៌មានកម្មវិធី"</string> <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# ទៀត}other{# ទៀត}}"</string> - <string name="old_sdk_deny_warning" msgid="2382236998845153919">"កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់ជាងនេះរបស់ Android ។ ការបដិសេធការអនុញ្ញាតអាចបណ្តាលឱ្យវាបំពេញមុខងារមិនដូចការគ្រោងទុកតទៅទៀតទេ។"</string> + <string name="old_sdk_deny_warning" msgid="2382236998845153919">"កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់ជាងនេះរបស់ Android ។ ការបដិសេធការអនុញ្ញាតអាចបណ្តាលឱ្យវាលែងអាចបំពេញមុខងារដូចការគ្រោងទុក។"</string> <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់ជាងនេះរបស់ Android។ ប្រសិនបើអ្នកផ្ដល់ការអនុញ្ញាតនេះ នោះសិទ្ធិចូលប្រើទំហំផ្ទុកទាំងអស់ (រូមទាំងរូបថត វីដេអូ តន្ត្រី សំឡេង និងឯកសារផ្សេងទៀត) នឹងត្រូវបានអនុញ្ញាត។"</string> <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់ជាងនេះរបស់ Android។ ប្រសិនបើអ្នកបដិសេធការអនុញ្ញាតនេះ នោះសិទ្ធិចូលប្រើទំហំផ្ទុកទាំងអស់ (រូមទាំងរូបថត វីដេអូ តន្ត្រី សំឡេង និងឯកសារផ្សេងទៀត) នឹងត្រូវបានបដិសេធ។"</string> <string name="default_permission_description" msgid="4624464917726285203">"ប្រតិបត្តិការសកម្មភាពមិនស្គាល់"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"ក្នុងករណីមានអាសន្ន"</string> <string name="role_home_label" msgid="3871847846649769412">"កម្មវិធីទំព័រដើមលំនាំដើម"</string> <string name="role_home_short_label" msgid="8544733747952272337">"កម្មវិធីទំព័រដើម"</string> - <string name="role_home_description" msgid="7997371519626556675">"កម្មវិធីដែលជាទូទៅហៅថាកម្មវិធីចាប់ផ្ដើម ដែលជំនួសអេក្រង់ដើមនៅលើឧបករណ៍ Android របស់អ្នក និងផ្ដល់លទ្ធភាពឱ្យអ្នកចូលប្រើខ្លឹមសារ និងមុខងាររបស់ឧបករណ៍អ្នក"</string> + <string name="role_home_description" msgid="7997371519626556675">"កម្មវិធីដែលគេច្រើនតែហៅថាកម្មវិធីចាប់ផ្ដើម វាជំនួសអេក្រង់ដើមនៅលើឧបករណ៍ Android របស់អ្នក និងផ្ដល់លទ្ធភាពឱ្យអ្នកចូលប្រើខ្លឹមសារ និងមុខងាររបស់ឧបករណ៍អ្នក"</string> <string name="role_home_request_title" msgid="738136983453341081">"កំណត់ <xliff:g id="APP_NAME">%1$s</xliff:g> ជាកម្មវិធីទំព័រដើមលំនាំដើមរបស់អ្នក?"</string> <string name="role_home_request_description" msgid="2658833966716057673">"មិនត្រូវការការអនុញ្ញាតទេ"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"កម្មវិធីចាប់ផ្ដើម"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"គ្មាន"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(លំនាំដើមប្រព័ន្ធ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"គ្មានកម្មវិធីទេ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"សេវាកម្ម NFC ផ្សេងទៀត"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"បានជ្រើសរើស"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"បានជ្រើសរើស - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ការចូលប្រើកម្មវិធីពិសេស"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"បានចូលប្រើម្សិលមិញនៅម៉ោង <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"បានចូលប្រើនៅថ្ងៃទី <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ពាក្យសម្ងាត់ប្រើបានតែម្ដងរបស់អ្នកគឺ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"អនុញ្ញាតការកំណត់ដែលបានដាក់កំហិត"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ការកំណត់ដែលបានដាក់កំហិត"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ដើម្បីសុវត្ថិភាពរបស់អ្នក បច្ចុប្បន្នមិនអាចប្រើការកំណត់នេះបានទេ។"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"មិនអាចធ្វើសកម្មភាពបានទេ ពេលកំពុងហៅទូរសព្ទ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ការអនុញ្ញាតឱ្យកម្មវិធីដំឡើងកម្មវិធីផ្សេងទៀតមិនត្រូវបានអនុញ្ញាតអំឡុងពេលហៅទូរសព្ទទេ។\n\n ជារឿយៗ ជនឆបោកស្នើសុំសកម្មភាពប្រភេទនេះអំឡុងពេលសន្ទនាតាមទូរសព្ទ ដូច្នេះសកម្មភាពនេះត្រូវបានទប់ស្កាត់ដើម្បីការពារអ្នក។ ប្រសិនបើអ្នកត្រូវបានណែនាំឱ្យធ្វើសកម្មភាពនេះដោយនរណាម្នាក់ដែលអ្នកមិនស្គាល់ វាអាចជាការឆបោក។"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"កម្មវិធីបានស្នើសុំសិទ្ធិចូលប្រើការអនុញ្ញាតដែលមានលក្ខណៈរសើប ដែលអាចធ្វើឱ្យព័ត៌មានហិរញ្ញវត្ថុ និងព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកប្រឈមនឹងហានិភ័យ។<xliff:g id="ID_1"><br><br></xliff:g>កម្មវិធីអាចនឹងមិនដំណើរការបានត្រឹមត្រូវទេ ប្រសិនបើគ្មានការអនុញ្ញាតដែលមានការរឹតបន្តឹងនេះ។ <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ស្វែងយល់អំពីរបៀបផ្ដល់សិទ្ធិចូលប្រើ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើជា<xliff:g id="ROLE_NAME">%1$s</xliff:g>លំនាំដើម"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"យល់ព្រម"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"បានទប់ស្កាត់សំណើសុំការអនុញ្ញាត"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"កម្មវិធីនេះកំពុងស្នើសុំការអនុញ្ញាតបន្ថែម ប៉ុន្តែមិនអាចផ្តល់ការអនុញ្ញាតក្នុងវគ្គផ្សាយបានទេ។ ផ្តល់ការអនុញ្ញាតនៅលើទូរសព្ទរបស់អ្នកជាមុនសិន។"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"សម្រាប់ការហៅ ឬផ្ញើសារជាអក្សរទៅលេខសង្គ្រោះបន្ទាន់"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"បានផ្ញើទីតាំងទៅសេវាសង្គ្រោះបន្ទាន់"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"កម្មវិធីនេះបានចូលប្រើទីតាំងឧបករណ៍របស់អ្នកក្នុងអំឡុងពេលហៅទូរសព្ទ ឬផ្ញើសារជាអក្សរទៅកាន់លេខសង្គ្រោះបន្ទាន់។ ករណីនេះអាចកើតឡើង ទោះបីជាកម្មវិធីនេះមិនមានការអនុញ្ញាតទីតាំង ឬបិទទីតាំងឧបករណ៍ក៏ដោយ។ "<a href="https://support.google.com/android/answer/9319337">"ស្វែងយល់បន្ថែម"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ពិនិត្យមើលទូរសព្ទរបស់អ្នក"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"មិនអាចភ្ជាប់ជាមួយទូរសព្ទរបស់អ្នកបានទេ"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ព្យាយាមម្ដងទៀតឬ?"</string> </resources> diff --git a/PermissionController/res/values-kn-v35/strings.xml b/PermissionController/res/values-kn-v35/strings.xml new file mode 100644 index 000000000..7205ee7fe --- /dev/null +++ b/PermissionController/res/values-kn-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ಭದ್ರತೆ"</string> +</resources> diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml index 5582af507..86f93bc4c 100644 --- a/PermissionController/res/values-kn/strings.xml +++ b/PermissionController/res/values-kn/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ಅನುಮತಿಗಳು"</string> <string name="cancel" msgid="8943320028373963831">"ರದ್ದುಮಾಡಿ"</string> <string name="back" msgid="6249950659061523680">"ಹಿಂದಕ್ಕೆ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ಮುಚ್ಚಿರಿ"</string> <string name="available" msgid="6007778121920339498">"ಲಭ್ಯವಿದೆ"</string> <string name="blocked" msgid="9195547604866033708">"ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string> <string name="on" msgid="280241003226755921">"ಆನ್"</string> @@ -277,7 +278,7 @@ <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP">%1$s</xliff:g> ಗೆ <xliff:g id="PERMISSION">%2$s</xliff:g> ಪ್ರವೇಶವನ್ನು ನೀಡಿದ್ದೀರಿ"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP">%1$s</xliff:g> ಗೆ <xliff:g id="PERMISSION_1">%2$s</xliff:g> ಮತ್ತು <xliff:g id="PERMISSION_2">%3$s</xliff:g> ಪ್ರವೇಶವನ್ನು ನೀಡಿದ್ದೀರಿ"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP">%2$s</xliff:g> ಗೆ <xliff:g id="COUNT">%1$d</xliff:g> ಅನುಮತಿಗಳನ್ನು ನೀಡಿದ್ದೀರಿ"</string> - <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_0">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡಿದ್ದೀರಿ}one{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_1">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡಿದ್ದೀರಿ}other{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_1">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡಿದ್ದೀರಿ}}"</string> + <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_0">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನೀಡಿದ್ದೀರಿ}one{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_1">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನೀಡಿದ್ದೀರಿ}other{ನೀವು ವಾಹನ ಚಲಾಯಿಸುವಾಗ, <xliff:g id="APP_1">%1$s</xliff:g> ಮತ್ತು # ಇತರ ಆ್ಯಪ್ಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನೀಡಿದ್ದೀರಿ}}"</string> <string name="go_to_settings" msgid="1053735612211228335">"ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ"</string> <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"ಹಲವಾರು ತಿಂಗಳುಗಳಿಂದ ಕೆಲವು ಆ್ಯಪ್ಗಳನ್ನು ಬಳಸಲಾಗಿಲ್ಲ"</string> <string name="permissions_removed_category_title" msgid="1064754271178447643">"ತೆಗೆದುಹಾಕಲಾದ ಅನುಮತಿಗಳು"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ಯಾವುದೂ ಬೇಡ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ಯಾವುದೇ ಆ್ಯಪ್ಗಳು ಇಲ್ಲ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ಇತರ NFC ಸೇವೆಗಳು"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g> - ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ವಿಶೇಷ ಆ್ಯಪ್ ಆ್ಯಕ್ಸೆಸ್"</string> @@ -581,7 +583,7 @@ <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"ಸ್ಥಿತಿ ಪರಿಶೀಲಿಸಿ"</string> <string name="privacy_controls_qs" msgid="5780144882040591169">"ನಿಮ್ಮ ಗೌಪ್ಯತೆ ನಿಯಂತ್ರಣಗಳು"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"ಇನ್ನಷ್ಟು ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> - <string name="camera_toggle_label_qs" msgid="3880261453066157285">"ಕ್ಯಾಮರಾ ಪ್ರವೇಶ"</string> + <string name="camera_toggle_label_qs" msgid="3880261453066157285">"ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್"</string> <string name="microphone_toggle_label_qs" msgid="8132912469813396552">"ಮೈಕ್ ಆ್ಯಕ್ಸೆಸ್"</string> <string name="permissions_removed_qs" msgid="8957319130625294572">"ಅನುಮತಿಯನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> <string name="camera_usage_qs" msgid="4394233566086665994">"ಇತ್ತೀಚಿನ ಕ್ಯಾಮರಾ ಬಳಕೆಯನ್ನು ನೋಡಿ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ನಿನ್ನೆ <xliff:g id="TIME_DATE">%1$s</xliff:g> ಸಮಯಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ಸಮಯಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ನಿಮ್ಮ ಒನ್-ಟೈಮ್ ಪಾಸ್ವರ್ಡ್ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಅನುಮತಿಸಿ"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಗಾಗಿ, ಈ ಸೆಟ್ಟಿಂಗ್ ಪ್ರಸ್ತುತ ಲಭ್ಯವಿಲ್ಲ."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ಫೋನ್ ಕರೆಯಲ್ಲಿರುವಾಗ ಕ್ರಿಯೆ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ಫೋನ್ ಕರೆಯ ಸಮಯದಲ್ಲಿ ಇತರ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಆ್ಯಪ್ಗಳನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ.\n\n ಫೋನ್ ಕರೆ ಸಂಭಾಷಣೆಯ ಸಮಯದಲ್ಲಿ ಸ್ಕ್ಯಾಮರ್ಗಳು ಆಗಾಗ್ಗೆ ಈ ರೀತಿಯ ಕ್ರಿಯೆಯನ್ನು ವಿನಂತಿಸುತ್ತಾರೆ, ಆದ್ದರಿಂದ ನಿಮ್ಮನ್ನು ರಕ್ಷಿಸಲು ಇದನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ. ನಿಮಗೆ ಪರಿಚಯವಿಲ್ಲದ ಯಾರಾದರೂ ಈ ಕ್ರಮವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ನಿಮಗೆ ಮಾರ್ಗದರ್ಶನ ನೀಡುತ್ತಿದ್ದರೆ, ಅದು ಸ್ಕ್ಯಾಮ್ ಆಗಿರಬಹುದು."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್ಗೆ ನಿರಾಕರಿಸಲಾಗಿದೆ"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮತ್ತು ಹಣಕಾಸಿನ ಮಾಹಿತಿಯನ್ನು ಅಪಾಯಕ್ಕೆ ಸಿಲುಕಿಸಬಹುದಾದ ಸೂಕ್ಷ್ಮ ಅನುಮತಿಗೆ ಆ್ಯಪ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ವಿನಂತಿಸಿದೆ.<xliff:g id="ID_1"><br><br></xliff:g>.ಈ ನಿರ್ಬಂಧಿತ ಅನುಮತಿಯಿಲ್ಲದೆ ಆ್ಯಪ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರುವ ಸಾಧ್ಯತೆಯಿದೆ. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೇಗೆ ಅನುಮತಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಿರಿ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ಡೀಫಾಲ್ಟ್ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ಆಗಿರಲು ಆ್ಯಪ್ಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ಸರಿ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ನಿಗ್ರಹಿಸಲಾಗಿದೆ"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ಈ ಆ್ಯಪ್ ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ, ಆದರೆ ಸ್ಟ್ರೀಮಿಂಗ್ ಸೆಶನ್ನಲ್ಲಿ ಅನುಮತಿಗಳನ್ನು ನೀಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮೊದಲು ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಅನುಮತಿ ನೀಡಿ."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ತುರ್ತು ಕರೆ ಅಥವಾ ಪಠ್ಯಕ್ಕಾಗಿ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ಸ್ಥಳವನ್ನು ತುರ್ತು ಸೇವೆಗಳಿಗೆ ಕಳುಹಿಸಲಾಗಿದೆ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ತುರ್ತು ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿದ ಅಥವಾ ಪಠ್ಯವನ್ನು ಕಳುಹಿಸುವ ಸಮಯದಲ್ಲಿ ಈ ಆ್ಯಪ್ ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿದೆ. ಆ್ಯಪ್ ಸ್ಥಳದ ಅನುಮತಿಯನ್ನು ಹೊಂದಿಲ್ಲದಿರುವಾಗ ಅಥವಾ ಸಾಧನದ ಸ್ಥಳವು ಆಫ್ ಆಗಿರುವಾಗಲೂ ಇದು ಸಂಭವಿಸಬಹುದು. "<a href="https://support.google.com/android/answer/9319337">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ಪುನಃ ಪ್ರಯತ್ನಿಸಬೇಕೆ?"</string> </resources> diff --git a/PermissionController/res/values-ko-v35/strings.xml b/PermissionController/res/values-ko-v35/strings.xml new file mode 100644 index 000000000..99de87a25 --- /dev/null +++ b/PermissionController/res/values-ko-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"보안"</string> +</resources> diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml index c77016ff2..35269a437 100644 --- a/PermissionController/res/values-ko/strings.xml +++ b/PermissionController/res/values-ko/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"권한"</string> <string name="cancel" msgid="8943320028373963831">"취소"</string> <string name="back" msgid="6249950659061523680">"뒤로"</string> + <string name="dialog_close" msgid="6840699812532384661">"닫기"</string> <string name="available" msgid="6007778121920339498">"허용됨"</string> <string name="blocked" msgid="9195547604866033708">"차단됨"</string> <string name="on" msgid="280241003226755921">"사용"</string> @@ -83,7 +84,7 @@ <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"이 앱은 Android 이전 버전에 맞게 설계되었습니다. 저장소 액세스 권한을 부여하면 앱에서 모든 저장소(사진, 동영상, 음악, 오디오 및 기타 파일 포함)에 액세스할 수 있습니다."</string> <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"이 앱은 Android 이전 버전에 맞게 설계되었습니다. 저장소 액세스 권한을 부여하지 않으면 앱에서 모든 저장소(사진, 동영상, 음악, 오디오 및 기타 파일)에 액세스할 수 없습니다."</string> <string name="default_permission_description" msgid="4624464917726285203">"알 수 없는 작업 실행"</string> - <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>개 앱 허용됨"</string> + <string name="app_permissions_group_summary" msgid="8788419008958284002">"앱 <xliff:g id="COUNT_1">%2$d</xliff:g>개 중 <xliff:g id="COUNT_0">%1$d</xliff:g>개 허용됨"</string> <string name="app_permissions_group_summary2" msgid="4329922444840521150">"앱 <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>개에 권한 부여됨"</string> <string name="menu_show_system" msgid="4254021607027872504">"시스템 표시"</string> <string name="menu_hide_system" msgid="3855390843744028465">"시스템 숨기기"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"없음"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(시스템 기본값)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"앱 없음"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"기타 NFC 서비스"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"선택됨"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"선택됨 - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"특수 앱 액세스 권한"</string> @@ -669,11 +671,14 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"어제 <xliff:g id="TIME_DATE">%1$s</xliff:g>에 액세스함"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>에 액세스함"</string> <string name="test_otp_msg" msgid="7559110574222727550">"일회용 비밀번호는 132435입니다"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"제한된 설정 허용"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"제한된 설정"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"보안을 위해 이 설정은 현재 사용할 수 없습니다."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"통화 중에 수행할 수 없는 작업입니다"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"통화 중에는 앱이 다른 앱을 설치하도록 허용할 수 없습니다.\n\n 사기범은 전화 통화 중에 종종 이러한 유형의 작업을 요청하므로, 사용자를 보호하기 위해 이러한 작업은 차단됩니다. 모르는 사람이 이 작업을 수행하도록 안내하는 경우 사기일 수도 있습니다."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>에 대한 앱의 액세스가 거부됨"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"앱에서 개인 정보 및 금융 정보가 위험에 노출될 수 있는 민감한 권한에 대한 액세스를 요청했습니다.<xliff:g id="ID_1"><br><br></xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>액세스 허용 방법 알아보기</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"앱을 기본 <xliff:g id="ROLE_NAME">%1$s</xliff:g>으로 사용하기 위한 앱의 액세스가 거부됨"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"앱을 기본 <xliff:g id="ROLE_NAME">%1$s</xliff:g>으로 사용하기 위한 액세스가 거부됨"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"앱에서 개인 정보 및 금융 정보가 위험에 노출될 수 있는 민감한 권한에 대한 액세스를 요청했습니다.<xliff:g id="ID_1"><br><br></xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>액세스 허용 방법 알아보기</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"앱의 액세스가 거부됨"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"이 권한에 액세스하면 개인 정보 및 금융 정보가 위험에 노출될 수 있습니다.<xliff:g id="ID_1"><br><br></xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>액세스 허용 방법 알아보기</a>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"확인"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"권한 요청 거부됨"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"앱에서 추가 권한을 요청합니다. 그러나 스트리밍 세션에서는 권한을 부여할 수 없습니다. 휴대전화에서 먼저 권한을 부여하세요."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"긴급 전화 또는 문자용"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"위치를 응급 서비스로 전송함"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"긴급 전화번호로 전화를 걸거나 문자를 전송하는 중에 앱에서 기기 위치에 액세스했습니다. 앱에 위치 정보 액세스 권한이 없거나 기기 위치 기능이 사용 중지되어 있는 경우에도 앱에서 기기 위치에 액세스할 수 있습니다. "<a href="https://support.google.com/android/answer/9319337">"자세히 알아보기"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"휴대전화를 확인하세요"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"휴대전화에 연결할 수 없습니다"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"다시 시도하시겠습니까?"</string> </resources> diff --git a/PermissionController/res/values-ky-v35/strings.xml b/PermissionController/res/values-ky-v35/strings.xml new file mode 100644 index 000000000..844abd6f0 --- /dev/null +++ b/PermissionController/res/values-ky-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Коопсуздук"</string> +</resources> diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml index 4aa6e4473..e1baac159 100644 --- a/PermissionController/res/values-ky/strings.xml +++ b/PermissionController/res/values-ky/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"уруксаттар"</string> <string name="cancel" msgid="8943320028373963831">"Жок"</string> <string name="back" msgid="6249950659061523680">"Артка"</string> + <string name="dialog_close" msgid="6840699812532384661">"Жабуу"</string> <string name="available" msgid="6007778121920339498">"Жеткиликтүү"</string> <string name="blocked" msgid="9195547604866033708">"Бөгөттөлдү"</string> <string name="on" msgid="280241003226755921">"Күйүк"</string> @@ -208,7 +209,7 @@ <string name="unused_apps_label_v2" msgid="7058776770056517980">"Колдонулбаган колдонмолордун ишин тындыруу"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Колдонмо колдонулбаса, аны тескеңиз"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Уруксаттар өчүрүлүп, убактылуу файлдар тазаланып, билдирмелер келбей калат"</string> - <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Уруксаттарды алып салып, убактылуу файлдарды жок кылып, билдирмелерди токтотуңуз жана колдонмону архивдеңиз"</string> + <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Уруксаттарды алып салып, убактылуу файлдарды жок кылып, билдирмелерди токтотуп, колдонмону архивдейсиз"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"Эгер колдонмо бир нече ай пайдаланылбаса, жеке маалыматтарыңызды коргоо үчүн бул колдонмого берилген уруксаттар өчүрүлөт."</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Эгер колдонмо бир нече ай пайдаланылбаса, жеке дайын-даректериңизди коргоо максатында төмөнкү уруксаттар өчүрүлөт: <xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Жеке дайын-даректериңизди коргоо максатында, бир нече айдан бери ачылбаган колдонмолордогу уруксаттар өчүрүлдү."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Жок"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Демейки тутум)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Бир да колдонмо жок"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Башка NFC кызматтары"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Тандалды"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Тандалды – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"колдонмонун атайын уруксаты"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> мүчүлүштүктөрдү оңдоо маалыматын жүктөп бергиси келет."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Мүчүлүштүктөр тууралуу кабарлансынбы?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Система бир мүчүлүштүктү аныктады."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"\"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" колдонмосу ушул түзмөктөн <xliff:g id="DATE">%2$s</xliff:g>, саат <xliff:g id="TIME">%3$s</xliff:g> түзүлгөн мүчүлүштүктөр тууралуу отчетту жүктөп берүүнү сурап жатат. Отчетто колдонуучулардын аттары, жүргөн жерлери, түзмөктүн идентификаторлору жана тармактын дайын-даректери сыяктуу түзмөгүңүздөгү же колдонмолоруңуздагы жеке маалымат камтылышы мүмкүн. Андыктан мүчүлүштүктөр тууралуу отчетту ишенимдүү адамдар жана колдонмолор менен гана бөлүшүңүз. \"<xliff:g id="APP_NAME_1">%4$s</xliff:g>\" колдонмосуна мүчүлүштүк тууралуу отчетту жүктөөгө уруксат бересизби?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"\"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" колдонмосу ушул түзмөктөн <xliff:g id="DATE">%2$s</xliff:g>, саат <xliff:g id="TIME">%3$s</xliff:g> түзүлгөн мүчүлүштүктөр тууралуу отчетту жүктөөгө уруксат сурап жатат. Отчетто колдонуучулардын аттары, жүргөн жерлери, түзмөктүн идентификаторлору жана тармактын дайын-даректери сыяктуу түзмөгүңүздөгү же колдонмолоруңуздагы жеке маалымат камтылышы мүмкүн. Андыктан мүчүлүштүктөр тууралуу отчетту ишенимдүү адамдар жана колдонмолор менен гана бөлүшүңүз. \"<xliff:g id="APP_NAME_1">%4$s</xliff:g>\" колдонмосу мүчүлүштүк тууралуу отчетту жүктөй берсинби?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы мүчүлүштүк тууралуу кабарды иштетүүдө ката кетти. Мүчүлүштүктөрдү аныктоо тууралуу кеңири маалыматтарды бөлүшүү сурамы четке кагылды. Ыңгайсыздык үчүн кечирим сурайбыз."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Уруксат берүү"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Тыюу салынат"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Кечээ саат <xliff:g id="TIME_DATE">%1$s</xliff:g> колдонулду"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> саат <xliff:g id="TIME_DATE_1">%2$s</xliff:g> колдонулду"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Бир жолу гана колдонулуучу кодуңуз: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Чектелген параметрлерге уруксат берүү"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Чектелген функция"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Коопсуздук максатында бул параметр азырынча иштебейт."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Телефон чалуу учурунда аракет жеткиликсиз"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Телефон чалуу учурунда колдонмолорго башка колдонмолорду орнотууга уруксат берүүгө болбойт.\n\n Телефон чалуу аркылуу сүйлөшүп жатканда шылуундар көбүнчө ушул сыяктуу аракетти аткарууну суранышат, андыктан коопсуздугуңузду коргоо үчүн бул нерсе бөгөттөлдү. Эгер сиз тааныбаган адам мындай аракетти аткарууну айтса, ал кесепчилик болушу мүмкүн."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Колдонмого <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> параметрин колдонууга тыюу салынды"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Колдонмо жеке жана каржы маалыматыңызга коркунуч жаратышы мүмкүн болгон купуя маалыматты көрүүгө уруксат сурады.<xliff:g id="ID_1"><br><br></xliff:g>Мындай уруксатсыз колдонмо ойдогудай иштебей коюшу мүмкүн. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Мүмкүнчүлүк берүү жөнүндө кеңири маалымат</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Колдонмого демейки <xliff:g id="ROLE_NAME">%1$s</xliff:g> ролуна тыюу салынды"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Жарайт"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Уруксат берүү сурамы четке кагылды"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Бул колдонмо кошумча уруксаттарды берүүнү суранып жатат, бирок алып ойнотуу сеансында уруксаттарды берүүгө болбойт. Адегенде телефондо уруксат бериңиз."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Шашылыш чалуу же билдирүү жөнөтүү үчүн"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Жайгашкан жер кырсыктаганда жардамга келчү кызматтарга жөнөтүлдү"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Бул колдонмо кырсыктаганда жардамга келчү кызматтын номерине чалып же билдирүү жөнөткөнүңүздө түзмөк жайгашкан жерди аныктады. Колдонмонун жайгашкан жерди аныктоого уруксаты болбосо же түзмөктүн жайгашкан жери өчүк болсо да, ушундай болушу мүмкүн. "<a href="https://support.google.com/android/answer/9319337">"Кеңири маалымат"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Телефонуңузду текшериңиз"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Телефонго туташкан жок"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Дагы аракет кыласызбы?"</string> </resources> diff --git a/PermissionController/res/values-lo-v35/strings.xml b/PermissionController/res/values-lo-v35/strings.xml new file mode 100644 index 000000000..08a6e9920 --- /dev/null +++ b/PermissionController/res/values-lo-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ຄວາມປອດໄພ"</string> +</resources> diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml index c7789fc2c..d3fe288a3 100644 --- a/PermissionController/res/values-lo/strings.xml +++ b/PermissionController/res/values-lo/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ສິດອະນຸຍາດ"</string> <string name="cancel" msgid="8943320028373963831">"ຍົກເລີກ"</string> <string name="back" msgid="6249950659061523680">"ກັບຄືນ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ປິດ"</string> <string name="available" msgid="6007778121920339498">"ສາມາດໃຊ້ໄດ້"</string> <string name="blocked" msgid="9195547604866033708">"ບລັອກແລ້ວ"</string> <string name="on" msgid="280241003226755921">"ເປີດຢູ່"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ບໍ່ມີ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ບໍ່ມີແອັບ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ບໍລິການ NFC ອື່ນໆ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ເລືອກແລ້ວ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ເລືອກແລ້ວ - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ສິດການເຂົ້າເຖິງແອັບພິເສດ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ເຂົ້າເຖິງມື້ວານນີ້ເມື່ອ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ເຂົ້າເຖິງເມື່ອ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ລະຫັດຜ່ານແບບໃຊ້ໄດ້ເທື່ອດຽວຂອງທ່ານແມ່ນ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"ອະນຸຍາດການຕັ້ງຄ່າທີ່ຈຳກັດ"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ການຕັ້ງຄ່າທີ່ຈຳກັດ"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ຕອນນີ້ຈຶ່ງບໍ່ສາມາດໃຊ້ການຕັ້ງຄ່ານີ້ໄດ້."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ການດຳເນີນການບໍ່ພ້ອມນຳໃຊ້ໃນຂະນະທີ່ກຳລັງໂທລະສັບ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ບໍ່ອະນຸຍາດໃຫ້ແອັບຕິດຕັ້ງແອັບອື່ນໆໃນລະຫວ່າງການໂທ.\n\n ສະແກມເມີມັກຈະຮ້ອງຂໍການດຳເນີນການປະເພດນີ້ໃນລະຫວ່າງການສົນທະນາທາງໂທລະສັບ, ດັ່ງນັ້ນລະບົບຈຶ່ງບລັອກໄວ້ເພື່ອປົກປ້ອງທ່ານ. ຫາກມີຄົນທີ່ທ່ານບໍ່ຮູ້ຈັກບອກໃຫ້ທ່ານດຳເນີນການດັ່ງກ່າວ, ນັ້ນອາດເປັນສະແກມ."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງຫາ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ແອັບໄດ້ຮ້ອງຂໍການສິດເຖິງການອະນຸຍາດທີ່ລະອຽດອ່ອນ ເຊິ່ງສາມາດເຮັດໃຫ້ຂໍ້ມູນສ່ວນຕົວ ແລະ ຂໍ້ມູນການເງິນຂອງທ່ານມີຄວາມສ່ຽງ.<xliff:g id="ID_1"><br><br></xliff:g>ມັນເປັນໄປໄດ້ວ່າແອັບດັ່ງກ່າວຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງໂດຍບໍ່ມີການອະນຸຍາດທີ່ຖືກຈຳກັດໄວ້ນີ້. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ສຶກສາວິທີອະນຸຍາດສິດເຂົ້າເຖິງ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງໃຫ້ເປັນ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ຄ່າເລີ່ມຕົ້ນ"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ຕົກລົງ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ຄຳຮ້ອງຂໍການອະນຸຍາດຖືກລະງັບ"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ແອັບນີ້ກຳລັງຮ້ອງຂໍການອະນຸຍາດເພີ່ມເຕີມ, ແຕ່ບໍ່ສາມາດໃຫ້ການອະນຸຍາດໃນເຊດຊັນການສະຕຣີມໄດ້. ກະລຸນາໃຫ້ການອະນຸຍາດໃນໂທລະສັບຂອງທ່ານກ່ອນ."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ສຳລັບການໂທ ຫຼື ສົ່ງຂໍ້ຄວາມສຸກເສີນ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ສົ່ງສະຖານທີ່ໄປຫາບໍລິການສຸກເສີນແລ້ວ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ແອັບນີ້ໄດ້ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນຂອງທ່ານໃນລະຫວ່າງທີ່ໂທ ຫຼື ສົ່ງຂໍ້ຄວາມຫາເບີໂທສຸກເສີນ. ເຊິ່ງນີ້ສາມາດເກີດຂຶ້ນໄດ້ເຖິງແມ່ນວ່າແອັບຈະບໍ່ມີການອະນຸຍາດສະຖານທີ່ ຫຼື ເຖິງແມ່ນວ່າສະຖານທີ່ຂອງອຸປະກອນຈະປິດຢູ່ກໍຕາມ. "<a href="https://support.google.com/android/answer/9319337">"ສຶກສາເພີ່ມເຕີມ"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ກວດເບິ່ງໂທລະສັບຂອງທ່ານ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ບໍ່ສາມາດເຂົ້າເຖິງໂທລະສັບຂອງທ່ານໄດ້"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ລອງໃໝ່ບໍ?"</string> </resources> diff --git a/PermissionController/res/values-lt-v35/strings.xml b/PermissionController/res/values-lt-v35/strings.xml new file mode 100644 index 000000000..b53fe3513 --- /dev/null +++ b/PermissionController/res/values-lt-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sauga"</string> +</resources> diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml index af06edb45..12718ca65 100644 --- a/PermissionController/res/values-lt/strings.xml +++ b/PermissionController/res/values-lt/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"leidimai"</string> <string name="cancel" msgid="8943320028373963831">"Atšaukti"</string> <string name="back" msgid="6249950659061523680">"Atgal"</string> + <string name="dialog_close" msgid="6840699812532384661">"Uždaryti"</string> <string name="available" msgid="6007778121920339498">"Pasiekiama"</string> <string name="blocked" msgid="9195547604866033708">"Užblokuota"</string> <string name="on" msgid="280241003226755921">"Įjungta"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nėra"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sistemos numatytoji programa)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nėra programų"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Kitos NFC paslaugos"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Pasirinkta"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Pasirinkta: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"speciali prieiga prie programų"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pasiekta vakar <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pasiekta <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jūsų vienkartinis slaptažodis yra 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Leisti apribotus nustatymus"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Apribotas nustatymas"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Šis nustatymas šiuo metu nepasiekiamas dėl jūsų saugumo."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Veiksmas nepasiekiamas vykstant pokalbiui telefonu"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Leidimas programoms įdiegti kitas programas neveikia vykstant pokalbiui telefonu.\n\n Sukčiai dažnai prašo atlikti tokio tipo veiksmus vykstant pokalbiui telefonu, todėl šis veiksmas blokuojamas siekiant jus apsaugoti. Jei nepažįstamas asmuo prašo atlikti šį veiksmą, tai gali būti sukčiavimas."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programai prieiga nesuteikta:<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programa pateikė leidimo pasiekti neskelbtiną informaciją, dėl kurio gali kilti pavojus jūsų asmens ir finansinei informacijai, užklausą.<xliff:g id="ID_1"><br><br></xliff:g>Gali būti, kad be šio apriboto leidimo programa neveiks tinkamai. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Sužinokite, kaip suteikti prieigą</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programai nesuteikta prieiga kaip numatytojo šio vaidmens: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Gerai"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Leidimo užklausa atmesta"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ši programa prašo papildomų leidimų, bet jų negalima suteikti per srautinio perdavimo seansą. Leidimą pirmiausia suteikite telefone."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Skambutis arba pranešimas pagalbos numeriu"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Vietovės informacija išsiųsta pagalbos tarnyboms"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Skambinant ar siunčiant pranešimą pagalbos numeriu, ši programa pasiekė jūsų įrenginio vietovės informaciją. Taip gali nutikti net tada, kai programa neturi leidimo pasiekti vietovės duomenis arba kai įrenginio vietovė yra išjungta."<a href="https://support.google.com/android/answer/9319337">"Sužinokite daugiau"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Patikrinkite telefoną"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nepavyksta pasiekti jūsų telefono"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Bandyti dar kartą?"</string> </resources> diff --git a/PermissionController/res/values-lv-v35/strings.xml b/PermissionController/res/values-lv-v35/strings.xml new file mode 100644 index 000000000..98ae9a138 --- /dev/null +++ b/PermissionController/res/values-lv-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Drošība"</string> +</resources> diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml index 32bcdd732..131aeff27 100644 --- a/PermissionController/res/values-lv/strings.xml +++ b/PermissionController/res/values-lv/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"atļaujas"</string> <string name="cancel" msgid="8943320028373963831">"Atcelt"</string> <string name="back" msgid="6249950659061523680">"Atpakaļ"</string> + <string name="dialog_close" msgid="6840699812532384661">"Aizvērt"</string> <string name="available" msgid="6007778121920339498">"Pieejama"</string> <string name="blocked" msgid="9195547604866033708">"Bloķēta"</string> <string name="on" msgid="280241003226755921">"Ieslēgta"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nav"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sistēmas noklusējums)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nav lietotņu"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Citi NFC pakalpojumi"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Atlasīta"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Atlasīta — <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Īpaša lietotņu piekļuve"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> vēlas augšupielādēt atkļūdošanas informāciju."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Vai kopīgot atkļūdošanas datus?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistēmā tika konstatēta problēma."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pieprasa augšupielādēt kļūdas pārskatu no šīs ierīces, kas veikts: <xliff:g id="DATE">%2$s</xliff:g> plkst. <xliff:g id="TIME">%3$s</xliff:g>. Kļūdu pārskatos ir ietverta personas informācija par jūsu ierīci vai lietotnēs reģistrēta informācija, piemēram, lietotājvārdi, atrašanās vietas dati, ierīču identifikatori un tīkla informācija. Kopīgojiet kļūdu pārskatus tikai ar lietotājiem un lietotnēm, kuriem uzticat šo informāciju. Vai atļaut lietotnei <xliff:g id="APP_NAME_1">%4$s</xliff:g> augšupielādēt kļūdas pārskatu?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pieprasa augšupielādēt kļūdas pārskatu no šīs ierīces, kas veikts: <xliff:g id="DATE">%2$s</xliff:g> plkst. <xliff:g id="TIME">%3$s</xliff:g>. Kļūdu pārskatos ir ietverta personas informācija par jūsu ierīci vai lietotnēs reģistrēta informācija, piemēram, lietotājvārdi, atrašanās vietas dati, ierīču identifikatori un tīkla informācija. Kopīgojiet kļūdu pārskatus tikai ar lietotājiem un lietotnēm, kuriem uzticat šo informāciju. Vai atļaut <xliff:g id="APP_NAME_1">%4$s</xliff:g> augšupielādēt kļūdas pārskatu?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Apstrādājot lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> kļūdas pārskatu, radās problēma. Tāpēc detalizēto atkļūdošanas datu kopīgošana tika liegta. Atvainojiet par traucējumu!"</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Atļaut"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Neatļaut"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pēdējā piekļuves reize vakar: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pēdējā piekļuves reize: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jūsu vienreizējā parole ir 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Atļaut ierobežotos iestatījumus"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ierobežots iestatījums"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Drošības apsvērumu dēļ šis iestatījums pašlaik nav pieejams."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Darbība nav pieejama tālruņa zvana laikā"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Tālruņa zvana laikā lietotnēm nav atļauts instalēt citas lietotnes.\n\n Šāda veida darbību tālruņa zvana laikā bieži pieprasa krāpnieki, tāpēc šī iespēja ir bloķēta, lai jūs aizsargātu. Ja jums nepazīstama persona jums iesaka veikt šo darbību, tas var būt krāpniecības mēģinājums."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Lietotnes piekļuve atļaujai “<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>” tika liegta"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Lietotne ir pieprasījusi piekļuvi sensitīvai atļaujai, kas var apdraudēt jūsu personas un finanšu informāciju.<xliff:g id="ID_1"><br><br></xliff:g>Iespējams, lietotne nedarbosies pareizi bez šīs ierobežotās atļaujas. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Uzziniet, kā piešķirt piekļuvi.</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Lietotnes piekļuve noklusējuma lomai “<xliff:g id="ROLE_NAME">%1$s</xliff:g>” tika liegta"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Labi"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Atļaujas pieprasījums bloķēts"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Šī lietotne pieprasa papildu atļaujas, taču atļaujas nevar piešķirt straumēšanas sesijā. Vispirms piešķiriet attiecīgo atļauju savā tālrunī."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Ārkārtas izsaukumam (zvanam vai īsziņai)"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ārkārtas palīdzības dienestiem tika nosūtīti atrašanās vietas dati"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Šī lietotne piekļuva ierīces atrašanās vietas datiem, zvanot vai sūtot īsziņu uz ārkārtas numuru. Tas var notikt pat tad, ja lietotnei nav atrašanās vietas atļaujas vai ierīcē ir izslēgta atrašanās vietas noteikšana. "<a href="https://support.google.com/android/answer/9319337">"Uzziniet vairāk."</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Pārbaudiet tālruni"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nevarēja sasniegt jūsu tālruni."</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vai mēģināt vēlreiz?"</string> </resources> diff --git a/PermissionController/res/values-mk-v35/strings.xml b/PermissionController/res/values-mk-v35/strings.xml new file mode 100644 index 000000000..3690e7e0e --- /dev/null +++ b/PermissionController/res/values-mk-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Безбедност"</string> +</resources> diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml index 48f1928fe..2f10498c5 100644 --- a/PermissionController/res/values-mk/strings.xml +++ b/PermissionController/res/values-mk/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"дозволи"</string> <string name="cancel" msgid="8943320028373963831">"Откажи"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Затвори"</string> <string name="available" msgid="6007778121920339498">"Дозволен"</string> <string name="blocked" msgid="9195547604866033708">"Блокиран"</string> <string name="on" msgid="280241003226755921">"Вклучено"</string> @@ -92,7 +93,7 @@ <string name="manage_permission" msgid="2895385393037061964">"Управувајте со дозволата"</string> <string name="no_apps" msgid="2412612731628386816">"Нема апликации"</string> <string name="location_settings" msgid="3624412509133422562">"Поставки за локација"</string> - <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> е давател на услуги според локација за овој уред. Пристапот до локацијата може да се смени од поставките за локација."</string> + <string name="location_warning" msgid="2381649060929040962">"<xliff:g id="APP_NAME">%1$s</xliff:g> е давател на локациски услуги за овој уред. Пристапот до локацијата може да се смени од поставките за локација."</string> <string name="system_warning" msgid="1173400963234358816">"Ако ја одбиете дозволава, основните функции на уредот можеби веќе нема да функционираат како што треба."</string> <string name="deny_read_media_visual_warning" msgid="3982586279917232827">"Оваа апликација е дизајнирана за постара верзија на Android. Ако го одбиете пристапот на апликацијава до фотографиите и видеата, пристапот до музиката и другото аудио исто така ќе се одбие."</string> <string name="deny_read_media_aural_warning" msgid="8928699919508646732">"Оваа апликација е дизајнирана за постара верзија на Android. Ако го одбиете пристапот на апликацијава до музиката и другото аудио, пристапот до фотографиите и видеата исто така ќе се одбие."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Нема"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Стандардно за системот)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Нема апликации"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Други услуги за NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Избрана"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Избрана - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"посебен пристап за апликации"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Пристапено вчера: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Пристапено: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Вашата еднократна лозинка е 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволете ограничени поставки"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена поставка"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"За ваша безбедност, поставкава е недостапна во моментов."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дејството не е достапно при телефонски повик"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Дозволувањето апликациите да инсталираат други апликации е оневозможено при телефонски повик.\n\n Измамниците често бараат ваков тип дејства при телефонските разговори, па блокирано е за да ве заштити. Ако некој што не го познавате ве води да го преземете дејствово, тоа може да биде измама."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Барањето за пристап на апликацијата до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> е одбиено"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликацијата побара пристап до дозвола за чувствителни податоци што може да ја загрози безбедноста на вашите лични и финансиски податоци.<xliff:g id="ID_1"><br><br></xliff:g>Можно е апликацијата да не функционира правилно без ограниченава дозвола. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Дознајте како да дозволите пристап</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Барањето на апликацијата да биде стандарднa <xliff:g id="ROLE_NAME">%1$s</xliff:g> е одбиено"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Во ред"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Барањето за дозвола е потиснато"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Апликацијава бара дополнителни дозволи, но нив не може да ги доделите во сесија за стриминг. Прво доделете ја дозволата на вашиот телефон."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"За итен повик или порака"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Локацијата е испратена до службите за итни случаи"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Оваа апликација пристапи до локацијата на вашиот уред при размена на повик или пораки со број за итни случаи. Ова може да се случи дури и кога апликацијата нема дозвола за локација или кога локацијата на уредот е исклучена. "<a href="https://support.google.com/android/answer/9319337">"Дознајте повеќе"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Проверете го телефонот"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Не можеше да се контактира со телефонот"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Ќе се обидете повторно?"</string> </resources> diff --git a/PermissionController/res/values-ml-v35/strings.xml b/PermissionController/res/values-ml-v35/strings.xml new file mode 100644 index 000000000..7d3552e5f --- /dev/null +++ b/PermissionController/res/values-ml-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"സുരക്ഷ"</string> +</resources> diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml index 94f0cefbd..e7ff97ea7 100644 --- a/PermissionController/res/values-ml/strings.xml +++ b/PermissionController/res/values-ml/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"അനുമതികൾ"</string> <string name="cancel" msgid="8943320028373963831">"റദ്ദാക്കുക"</string> <string name="back" msgid="6249950659061523680">"മടങ്ങുക"</string> + <string name="dialog_close" msgid="6840699812532384661">"അടയ്ക്കുക"</string> <string name="available" msgid="6007778121920339498">"ലഭ്യമാണ്"</string> <string name="blocked" msgid="9195547604866033708">"ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string> <string name="on" msgid="280241003226755921">"ഓണാണ്"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ഒന്നുമില്ല"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(സിസ്റ്റം ഡിഫോൾട്ട്)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ആപ്പുകൾ ഒന്നുമില്ല"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"മറ്റ് NFC സേവനങ്ങൾ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"തിരഞ്ഞെടുത്തു"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g> - തിരഞ്ഞെടുത്തു"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"പ്രത്യേക ആപ്പ് ആക്സസ്"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ഇന്നലെ <xliff:g id="TIME_DATE">%1$s</xliff:g>-ന് ആക്സസ് ചെയ്തു"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>-ന് ആക്സസ് ചെയ്തു"</string> <string name="test_otp_msg" msgid="7559110574222727550">"നിങ്ങളുടെ ഒറ്റത്തവണ പാസ്വേഡ് 132435 ആണ്"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"നിയന്ത്രിത ക്രമീകരണം അനുവദിക്കുക"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"നിയന്ത്രിത ക്രമീകരണം"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ക്രമീകരണം നിലവിൽ ലഭ്യമല്ല."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ഫോൺ കോളിനിടെ പ്രവർത്തനം ലഭ്യമല്ല"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ഫോൺ കോളിനിടെ മറ്റ് ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാനുള്ള അനുവാദം ആപ്പുകൾക്കില്ല.\n\n ഫോൺ കോൾ സംഭാഷണത്തിനിടെ സ്കാമർമാർ പലപ്പോഴും ഇത്തരത്തിലുള്ള പ്രവർത്തനം അഭ്യർത്ഥിക്കാറുണ്ട്, അതിനാൽ നിങ്ങളെ പരിരക്ഷിക്കുന്നതിനായി ഇത് ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു. പരിചയമില്ലാത്ത ആരെങ്കിലുമാണ് ഈ പ്രവർത്തനം നിങ്ങളോട് നിർദ്ദേശിക്കുന്നതെങ്കിൽ, അതൊരു സ്കാം ആയേക്കാം."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ആപ്പിന് <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> എന്നതിലേക്കുള്ള ആക്സസ് നിരസിച്ചു"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ആപ്പ് സെൻസിറ്റീവ് വിവരങ്ങൾക്കുള്ള അനുമതിയിലേക്ക് ആക്സസ് അഭ്യർത്ഥിച്ചു, ഇത് നിങ്ങളുടെ വ്യക്തിപരവും സാമ്പത്തികവുമായ വിവരങ്ങളെ അപകടത്തിലാക്കിയേക്കാം.<xliff:g id="ID_1"><br><br></xliff:g>ഈ നിയന്ത്രിത അനുമതിയില്ലാതെ ആപ്പ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ആക്സസ് എങ്ങനെ അനുവദിക്കുന്നുവെന്നറിയുക</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ആപ്പിന് ഡിഫോൾട്ട് <xliff:g id="ROLE_NAME">%1$s</xliff:g> ആകാനുള്ള ആക്സസ് നിരസിച്ചു"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ശരി"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"അനുമതി അഭ്യർത്ഥന കാഴ്ച്ചയിൽ നിന്ന് മറച്ചു"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ഈ ആപ്പ് അധിക അനുമതികൾ അഭ്യർത്ഥിക്കുന്നു, എന്നാൽ സ്ട്രീമിംഗ് സെഷനിടയിൽ അനുമതികൾ നൽകാനാകില്ല. ആദ്യം നിങ്ങളുടെ ഫോണിൽ അനുമതി നൽകുക."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"എമർജൻസി കോളിനോ ടെക്സ്റ്റിനോ ആയി"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"അടിയന്തര സേവനങ്ങളിലേക്ക് ലൊക്കേഷൻ അയച്ചു"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"അടിയന്തര നമ്പറിലേക്ക് ഒരു കോൾ ചെയ്യുമ്പോഴോ ടെക്സ്റ്റ് ചെയ്യുമ്പോഴോ ഈ ആപ്പ് നിങ്ങളുടെ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്തു. ആപ്പിന് ലൊക്കേഷൻ അനുമതി ഇല്ലാത്തപ്പോഴോ ഉപകരണ ലൊക്കേഷൻ ഓഫായിരിക്കുമ്പോഴോ പോലും ഇത് സംഭവിക്കാം. "<a href="https://support.google.com/android/answer/9319337">"കൂടുതലറിയുക"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"നിങ്ങളുടെ ഫോൺ പരിശോധിക്കുക"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"നിങ്ങളുടെ ഫോണുമായി കണക്റ്റ് ചെയ്യാനായില്ല"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"വീണ്ടും ശ്രമിക്കണോ?"</string> </resources> diff --git a/PermissionController/res/values-mn-v35/strings.xml b/PermissionController/res/values-mn-v35/strings.xml new file mode 100644 index 000000000..cb9e18162 --- /dev/null +++ b/PermissionController/res/values-mn-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Аюулгүй байдал"</string> +</resources> diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml index ea1b653e3..0330dc6d2 100644 --- a/PermissionController/res/values-mn/strings.xml +++ b/PermissionController/res/values-mn/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"зөвшөөрөл"</string> <string name="cancel" msgid="8943320028373963831">"Болих"</string> <string name="back" msgid="6249950659061523680">"Буцах"</string> + <string name="dialog_close" msgid="6840699812532384661">"Хаах"</string> <string name="available" msgid="6007778121920339498">"Боломжтой"</string> <string name="blocked" msgid="9195547604866033708">"Блоклосон"</string> <string name="on" msgid="280241003226755921">"Асаалттай"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Тохируулсан апп алга"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Системийн өгөгдмөл)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Апп алга"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Бусад NFC үйлчилгээ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Сонгосон"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Сонгосон - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"аппын тусгай хандалт"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Өчигдөр <xliff:g id="TIME_DATE">%1$s</xliff:g>-д хандсан"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>-д хандсан"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Таны нэг удаагийн нууц үг бол 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Хязгаарлагдсан тохиргоог зөвшөөрөх"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Хязгаарлагдсан тохиргоо"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Таны аюулгүй байдлын үүднээс энэ тохиргоо одоогоор боломжгүй байна."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Утасны дуудлага хийж байхад үйлдэл хийх боломжгүй"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Утасны дуудлагын үеэр аппуудад бусад апп суулгахыг зөвшөөрдөггүй.\n\n Залилагчид утасны дуудлагын харилцан ярианы үеэр ийм төрлийн үйлдлийг ихэвчлэн хүсдэг тул таныг хамгаалахаар уг үйлдлийг блоклосон. Хэрэв таны танихгүй хүн ийм үйлдэл хийлгэхээр таныг чиглүүлж байгаа бол энэ нь заль мэх байж болно."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Аппад <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>-д хандах эрх олгохоос татгалзсан"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апп нь таны хувийн болон санхүүгийн мэдээллийг эрсдэлд оруулж болох эмзэг зөвшөөрөлд хандах эрх хүссэн.<xliff:g id="ID_1"><br><br></xliff:g>Энэ хязгаарлагдмал зөвшөөрөлгүйгээр уг апп зохих ёсоор ажиллахгүй байх боломжтой. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Хандах эрхийг хэрхэн зөвшөөрөх талаар мэдэж авах</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Аппад өгөгдмөл <xliff:g id="ROLE_NAME">%1$s</xliff:g> болох эрх олгохоос татгалзсан"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Зөвшөөрлийн хүсэлтийг зогсоосон"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Энэ апп нэмэлт зөвшөөрөл хүсэж байгаа ч дамжуулалтын харилцан үйлдлийн үеэр зөвшөөрөл олгох боломжгүй. Эхлээд утсан дээрээ зөвшөөрөл олгоно уу."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Яаралтай дуудлага хийх эсвэл мессеж илгээх зорилгоор"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Байршлыг яаралтай тусламжийн үйлчилгээнүүд рүү илгээсэн"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Энэ апп яаралтай тусламжийн утасны дугаар луу хийсэн дуудлага эсвэл илгээсэн мессежийн үеэр таны төхөөрөмжийн байршилд хандсан. Энэ нь аппад байршлын зөвшөөрөл байхгүй эсвэл төхөөрөмжийн байршил унтраалттай байсан ч тохиолдож болно. "<a href="https://support.google.com/android/answer/9319337">"Нэмэлт мэдээлэл авах"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Утсаа шалгах"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Таны утастай холбогдож чадсангүй"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Дахин оролдох уу?"</string> </resources> diff --git a/PermissionController/res/values-mr-v35/strings.xml b/PermissionController/res/values-mr-v35/strings.xml new file mode 100644 index 000000000..3d8a7dedf --- /dev/null +++ b/PermissionController/res/values-mr-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"सुरक्षा"</string> +</resources> diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml index ce1621bbc..8f8172131 100644 --- a/PermissionController/res/values-mr/strings.xml +++ b/PermissionController/res/values-mr/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"परवानग्या"</string> <string name="cancel" msgid="8943320028373963831">"रद्द करा"</string> <string name="back" msgid="6249950659061523680">"मागे जा"</string> + <string name="dialog_close" msgid="6840699812532384661">"बंद करा"</string> <string name="available" msgid="6007778121920339498">"उपलब्ध आहे"</string> <string name="blocked" msgid="9195547604866033708">"ब्लॉक केला आहे"</string> <string name="on" msgid="280241003226755921">"सुरू आहे"</string> @@ -86,7 +87,7 @@ <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_1">%2$d</xliff:g> पैकी <xliff:g id="COUNT_0">%1$d</xliff:g> अॅप्सना परवानगी दिली"</string> <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> अॅप्सना अनुमती आहे"</string> <string name="menu_show_system" msgid="4254021607027872504">"सिस्टीम दाखवा"</string> - <string name="menu_hide_system" msgid="3855390843744028465">"सिस्टम लपवा"</string> + <string name="menu_hide_system" msgid="3855390843744028465">"सिस्टीम लपवा"</string> <string name="menu_show_7_days_data" msgid="8979611198508523706">"सात दिवस दाखवा"</string> <string name="menu_show_24_hours_data" msgid="8228054833323380780">"२४ तास दाखवा"</string> <string name="manage_permission" msgid="2895385393037061964">"परवानगी व्यवस्थापित करा"</string> @@ -208,7 +209,7 @@ <string name="unused_apps_label_v2" msgid="7058776770056517980">"न वापरल्यास अॅप अॅक्टिव्हिटी थांबवा"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"वापरले नसल्यास ॲप व्यवस्थापित करा"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"परवानग्या काढून टाका, तात्पुरत्या फाइल हटवा आणि सूचना थांबवा"</string> - <string name="unused_apps_summary_v2" msgid="5011313200815115802">"परवानग्या काढून टाका, तात्पुरत्या फाइल हटवा, सूचना थांबवा आणि ॲप संग्रहित करा"</string> + <string name="unused_apps_summary_v2" msgid="5011313200815115802">"परवानग्या काढून टाका, तात्पुरत्या फाइल हटवा, नोटिफिकेशन थांबवा आणि ॲप संग्रहित करा"</string> <string name="auto_revoke_summary" msgid="5867548789805911683">"तुमच्या डेटाचे संरक्षण करण्यासाठी, अॅप काही महिन्यांत वापरले गेले नसल्यास, या अॅपच्या परवानग्या काढल्या जातील."</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"तुमच्या डेटाचे संरक्षण करण्यासाठी, अॅप काही महिन्यांत वापरले गेले नसल्यास, पुढील परवानग्या काढल्या जातील: <xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"तुमच्या डेटाचे संरक्षण करण्यासाठी, तुम्ही काही महिन्यांत न वापरलेल्या ॲप्समधून परवानग्या काढल्या गेल्या आहेत."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"काहीही नाही"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डीफॉल्ट)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"अॅप्स नाहीत"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"इतर NFC सेवा"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"निवडले"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"निवडले - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"विशेष अॅप अॅक्सेस"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"काल <xliff:g id="TIME_DATE">%1$s</xliff:g> वाजता ॲक्सेस केले"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> ला <xliff:g id="TIME_DATE_1">%2$s</xliff:g> वाजता ॲक्सेस केले"</string> <string name="test_otp_msg" msgid="7559110574222727550">"तुमचा वन टाइम पासवर्ड १३२४३५ हा आहे"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"प्रतिबंधित सेटिंग्जना अनुमती द्या"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबंधित सेटिंग"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तुमच्या सुरक्षेसाठी, हे सेटिंग सध्या उपलब्ध नाही."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फोन कॉलवर असताना कृती उपलब्ध नाही"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फोन कॉलदरम्यान ॲप्सना इतर ॲप्स इंस्टॉल करण्याची अनुमती नाही.\n\n घोटाळेबाज व्यक्ती फोन कॉल संभाषणांदरम्यान, याप्रकारच्या कृतीची विनंती करतात, यामुळे तुमचे संरक्षण करण्यासाठी ती ब्लॉक केली आहे. तुम्ही ओळखत नसलेल्या एखाद्या व्यक्तीने ही कारवाई करण्यासाठी तुम्हाला मार्गदर्शन केले जात असल्यास, हा घोटाळा असू शकतो."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"अॅपचा <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> साठीचा अॅक्सेस नाकारला गेला आहे"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ॲपने संवेदनशील परवानगीसाठी अॅक्सेसची विनंती केली आहे, ज्यामुळे तुमची वैयक्तिक आणि आर्थिक माहिती धोक्यात येऊ शकते.<xliff:g id="ID_1"><br><br></xliff:g>या प्रतिबंधित परवानगीशिवाय ॲप कदाचित योग्यरीत्या काम करणार नाही. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>अॅक्सेसला अनुमती कशी द्यावी हे जाणून घ्या</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"अॅपचा डीफॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> असण्यासाठीचा अॅक्सेस नाकारला गेला आहे"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ओके"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"परवानगी मागणारी विनंती सप्रेस केली आहे"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"हे ॲप अतिरिक्त परवानग्यांची विनंती करत आहे, पण स्ट्रीमिंग सेशनदरम्यान परवानग्या दिल्या जाऊ शकत नाहीत. आधी तुमच्या फोनवर परवानगी द्या."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"आणीबाणी कॉल करण्यासाठी किंवा एसएमएस पाठवण्यासाठी"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"आणीबाणी सेवांना स्थान पाठवले आहे"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"आणीबाणी नंबरवर कॉल करत असताना किंवा एसएमएस पाठवत असताना या ॲपने तुमच्या डिव्हाइसचे स्थान ॲक्सेस केले आहे. ॲपकडे स्थान परवानगी नसताना किंवा डिव्हाइसचे स्थान बंद असतानादेखील हे होऊ शकते. "<a href="https://support.google.com/android/answer/9319337">"अधिक जाणून घ्या"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"तुमचा फोन तपासा"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"तुमच्या फोनशी कनेक्ट करता आले नाही"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"पुन्हा प्रयत्न करायचा आहे का?"</string> </resources> diff --git a/PermissionController/res/values-ms-v35/strings.xml b/PermissionController/res/values-ms-v35/strings.xml new file mode 100644 index 000000000..f09b6ea0e --- /dev/null +++ b/PermissionController/res/values-ms-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Keselamatan"</string> +</resources> diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml index d12d0797c..420e6244c 100644 --- a/PermissionController/res/values-ms/strings.xml +++ b/PermissionController/res/values-ms/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"kebenaran"</string> <string name="cancel" msgid="8943320028373963831">"Batal"</string> <string name="back" msgid="6249950659061523680">"Kembali"</string> + <string name="dialog_close" msgid="6840699812532384661">"Tutup"</string> <string name="available" msgid="6007778121920339498">"Tersedia"</string> <string name="blocked" msgid="9195547604866033708">"Disekat"</string> <string name="on" msgid="280241003226755921">"Hidup"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Tiada"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Ciri lalai sistem)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Tiada apl"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Perkhidmatan NFC yang lain"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Dipilih"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Dipilih - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"akses apl khas"</string> @@ -669,16 +671,25 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Diakses semalam <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Diakses <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Kata laluan sekali guna anda ialah 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Benarkan tetapan terhad"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Tetapan terhad"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Untuk keselamatan anda, tetapan ini tidak tersedia pada masa ini."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Tindakan tidak tersedia semasa panggilan telefon"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Tindakan membenarkan apl memasang apl lain adalah tidak dibenarkan semasa panggilan telefon.\n\n Penipu sering meminta jenis tindakan ini semasa perbualan panggilan telefon, maka tindakan ini disekat untuk melindungi anda. Jika anda dibimbing untuk mengambil tindakan ini oleh seseorang yang anda tidak kenali, perkara ini mungkin merupakan komplot."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Akses apl kepada <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> telah ditolak"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1"><br><br></xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ketahui cara membenarkan akses</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Akses apl untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> secara lalai telah ditolak"</string> - <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1"><br><br></xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ketahui cara membenarkan akses</a>"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Akses apl untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> lalai telah ditolak"</string> + <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1"><br><br></xliff:g>Apl mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ketahui cara membenarkan akses</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Akses apl telah ditolak"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Akses kepada kebenaran ini boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1"><br><br></xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ketahui cara membenarkan akses</a>"</string> <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ketahui lebih lanjut"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permintaan kebenaran disekat"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Apl ini meminta kebenaran tambahan tetapi kebenaran tidak boleh diberikan dalam sesi penstriman. Berikan kebenaran pada telefon anda dahulu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Untuk panggilan atau teks kecemasan"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokasi dihantar kepada perkhidmatan kecemasan"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Apl ini mengakses lokasi peranti anda semasa panggilan atau teks kepada nombor kecemasan. Hal ini boleh berlaku apabila apl tiada kebenaran lokasi atau lokasi peranti telah dimatikan. "<a href="https://support.google.com/android/answer/9319337">"Ketahui lebih lanjut"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Semak telefon anda"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Tidak dapat menyambung kepada telefon anda"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Cuba semula?"</string> </resources> diff --git a/PermissionController/res/values-my-v35/strings.xml b/PermissionController/res/values-my-v35/strings.xml new file mode 100644 index 000000000..ef994e0a4 --- /dev/null +++ b/PermissionController/res/values-my-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"လုံခြုံရေး"</string> +</resources> diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml index b5f28bd1b..df6541f42 100644 --- a/PermissionController/res/values-my/strings.xml +++ b/PermissionController/res/values-my/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ခွင့်ပြုချက်များ"</string> <string name="cancel" msgid="8943320028373963831">"မလုပ်တော့"</string> <string name="back" msgid="6249950659061523680">"နောက်သို့"</string> + <string name="dialog_close" msgid="6840699812532384661">"ပိတ်ရန်"</string> <string name="available" msgid="6007778121920339498">"ရနိုင်သည်"</string> <string name="blocked" msgid="9195547604866033708">"ပိတ်ထားသည်"</string> <string name="on" msgid="280241003226755921">"ဖွင့်"</string> @@ -194,7 +195,7 @@ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"ကန့်သတ်သုံးခြင်းကို ခွင့်ပြုရန်"</string> <string name="precise_image_description" msgid="6349638632303619872">"နေရာအတိအကျ"</string> <string name="approximate_image_description" msgid="938803699637069884">"တည်နေရာခန့်မှန်းခြေ"</string> - <string name="app_permission_location_accuracy" msgid="7166912915040018669">"နေရာအတိအကျကို သုံးခြင်း"</string> + <string name="app_permission_location_accuracy" msgid="7166912915040018669">"နေရာအတိအကျ သုံးရန်"</string> <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"နေရာအတိအကျကို ပိတ်ထားသည့်အခါ အက်ပ်များက သင်၏တည်နေရာခန့်မှန်းခြေကို သုံးနိုင်သည်"</string> <string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> ခွင့်ပြုချက်"</string> <string name="app_permission_header" msgid="2951363137032603806">"ဤအက်ပ်အတွက် <xliff:g id="PERM">%1$s</xliff:g> အသုံးပြုခွင့်"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"ရေခဲ"</string> <string name="role_home_label" msgid="3871847846649769412">"မူရင်း ပင်မစာမျက်နှာအက်ပ်"</string> <string name="role_home_short_label" msgid="8544733747952272337">"ပင်မစာမျက်နှာ အက်ပ်"</string> - <string name="role_home_description" msgid="7997371519626556675">"သင့် Android စက်ပစ္စည်းပေါ်ရှိ \'ပင်မစာမျက်နှာများ\' ကိုအစားထိုးသော ဖွင့်စနစ်များဟု ခေါ်လေ့ရှိသည့် အက်ပ်များနှင့် သင့်စက်ပစ္စည်း၏ အကြောင်းအရာနှင့် ဝန်ဆောင်မှုများသို့ ဝင်သုံးခွင့်ပေးသော အက်ပ်များ"</string> + <string name="role_home_description" msgid="7997371519626556675">"သင့်စက်၏ အကြောင်းအရာနှင့် တူးလ်များကို သုံးခွင့်ပေးပြီး သင့် Android စက်ပစ္စည်းပေါ်ရှိ \'ပင်မစာမျက်နှာများ\' ကိုအစားထိုးသော ဖွင့်စနစ်များဟု ခေါ်လေ့ရှိသည့် အက်ပ်များ"</string> <string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို သင့်မူရင်း ပင်မစာမျက်နှာ အက်ပ်အဖြစ် သတ်မှတ်လိုပါသလား။"</string> <string name="role_home_request_description" msgid="2658833966716057673">"ခွင့်ပြုချက် မလိုပါ"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"ဖွင့်တင်ပေးသူ"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"မရှိ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(စနစ်မူရင်း)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"အက်ပ် မရှိပါ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"အခြား NFC ဝန်ဆောင်မှုများ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ရွေးထားသည်"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ရွေးထားသည် - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"အက်ပ် အထူးအသုံးပြုခွင့်"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"မနေ့ <xliff:g id="TIME_DATE">%1$s</xliff:g> တွင် ဝင်ကြည့်ထားသည်"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g> တွင် ဝင်ကြည့်ထားသည်"</string> <string name="test_otp_msg" msgid="7559110574222727550">"သင့်တစ်ခါသုံး စကားဝှက်သည် 132435 ဖြစ်သည်"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"ကန့်သတ်ဆက်တင်များ ခွင့်ပြုရန်"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ကန့်သတ်ဆက်တင်"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"သင့်လုံခြုံရေးအတွက် ဤဆက်တင်ကို လောလောဆယ် မရနိုင်ပါ။"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ဖုန်းခေါ်နေစဉ်အတွင်း လုပ်ဆောင်ချက်များ မရနိုင်ပါ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ဖုန်းခေါ်နေစဉ်အတွင်း အခြားအက်ပ်များ ထည့်သွင်းရန် အက်ပ်များအား ခွင့်ပြုခြင်းကို လုပ်ခွင့်မပြုပါ။\n\n လူလိမ်များသည် ဤကဲ့သို့ လုပ်ဆောင်ချက်ကို ဖုန်းပြောဆိုနေစဉ်အတွင်း တောင်းဆိုလေ့ရှိကြသောကြောင့် သင့်အား ကာကွယ်ပေးရန် ၎င်းကိုပိတ်ထားသည်။ သင်မသိသော ပုဂ္ဂိုလ်တစ်ဦးဦးက ဤလုပ်ဆောင်ချက်ကို ဆောင်ရွက်ရန် သင့်အားပြောဆိုနေပါက လိမ်လည်မှုဖြစ်နိုင်သည်။"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"အက်ပ်ကို <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> သုံးခွင့် ငြင်းပယ်ထားသည်"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"အက်ပ်သည် သင်၏ ပုဂ္ဂိုလ်ရေးနှင့် ငွေကြေးဆိုင်ရာ အချက်အလက်များကို အန္တရာယ်ဖြစ်စေနိုင်သော သတိထားရမည့် ခွင့်ပြုချက်သုံးရန် တောင်းဆိုထားသည်။<xliff:g id="ID_1"><br><br></xliff:g>အက်ပ်သည် ဤကန့်သတ်ထားသော ခွင့်ပြုချက်မရှိပါက ကောင်းစွာမလုပ်ဆောင်ခြင်း ဖြစ်နိုင်သည်။ <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>သုံးခွင့်ပြုနည်းကို လေ့လာရန်</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"အက်ပ်အား မူရင်း <xliff:g id="ROLE_NAME">%1$s</xliff:g> အဖြစ် လုပ်ဆောင်ခွင့် ငြင်းပယ်ထားသည်"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ခွင့်ပြုချက်တောင်းဆိုမှု ပိတ်ထားသည်"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ဤအက်ပ်သည် နောက်ထပ်ခွင့်ပြုချက်များကို တောင်းဆိုနေသော်လည်း တိုက်ရိုက်လွှင့်စက်ရှင်တွင် ခွင့်ပြုချက်များ ပေး၍မရပါ။ သင့်ဖုန်းတွင် ဦးစွာ ခွင့်ပြုချက်ပေးပါ။"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"အရေးပေါ်ဖုန်းခေါ်ခြင်း (သို့) စာတိုပို့ခြင်းအတွက်"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"အရေးပေါ်ဝန်ဆောင်မှုများထံ တည်နေရာ ပို့ထားသည်"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"အရေးပေါ် နံပါတ်ထံ ဖုန်းခေါ် (သို့) စာတိုပို့နေစဉ် ဤအက်ပ်သည် သင့်စက်ပစ္စည်း၏ တည်နေရာကို သုံးထားသည်။ အက်ပ်တွင် တည်နေရာခွင့်ပြုချက် မရှိလည်း (သို့) စက်ပစ္စည်းတည်နေရာကို ပိတ်ထားလည်း ဤသို့ဖြစ်နိုင်သည်။ "<a href="https://support.google.com/android/answer/9319337">"ပိုမိုလေ့လာရန်"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"သင့်ဖုန်းကို စစ်ဆေးပါ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"သင့်ဖုန်းကို ချိတ်ဆက်၍မရပါ"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ထပ်စမ်းကြည့်မလား။"</string> </resources> diff --git a/PermissionController/res/values-nb-v35/strings.xml b/PermissionController/res/values-nb-v35/strings.xml new file mode 100644 index 000000000..0e03b1f53 --- /dev/null +++ b/PermissionController/res/values-nb-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Sikkerhet"</string> +</resources> diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml index be7157d55..26f134e5d 100644 --- a/PermissionController/res/values-nb/strings.xml +++ b/PermissionController/res/values-nb/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"tillatelser"</string> <string name="cancel" msgid="8943320028373963831">"Avbryt"</string> <string name="back" msgid="6249950659061523680">"Tilbake"</string> + <string name="dialog_close" msgid="6840699812532384661">"Lukk"</string> <string name="available" msgid="6007778121920339498">"Tilgjengelig"</string> <string name="blocked" msgid="9195547604866033708">"Blokkert"</string> <string name="on" msgid="280241003226755921">"På"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ingen"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(System-&shy;standard)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ingen apper"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andre NFC-tjenester"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valgt"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Valgt – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"spesiell apptilgang"</string> @@ -669,11 +671,14 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Åpnet i går <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Åpnet <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Engangspassordet ditt er 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Tillat begrensede innstillinger"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrenset innstilling"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av sikkerhetshensyn er denne innstillingen utilgjengelig for øyeblikket."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Handlingen er utilgjengelig under telefonsamtaler"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det er ikke tillatt å la apper installere andre apper under telefonsamtaler.\n\n Svindlere ber ofte om slike handlinger under telefonsamtaler, så det er blokkert for å beskytte deg. Hvis noen du ikke kjenner, ber deg om å gjøre dette, kan det være svindel."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appens tilgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ble avvist"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen ba om tilgang til en sensitiv tillatelse som kan utsette den personlige og økonomiske informasjonen din for fare.<xliff:g id="ID_1"><br><br></xliff:g>Det kan hende at appen ikke fungerer skikkelig uten denne begrensede tillatelsen. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Finn ut hvordan du gir tilgang</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appens tilgang til standard <xliff:g id="ROLE_NAME">%1$s</xliff:g>, ble avvist"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen fikk ikke tillatelse til å være standard <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Appen ba om tilgang til sensitive tillatelser som kan utsette den personlige og økonomiske informasjonen din for fare.<xliff:g id="ID_1"><br><br></xliff:g>Det kan hende at appen ikke fungerer skikkelig uten disse begrensede tillatelsene. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Finn ut hvordan du gir tilgang</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Appens tilgang ble avvist"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Hvis du gir tilgang til denne tillatelsen, kan den personlige og økonomiske informasjonen din bli utsatt for fare.<xliff:g id="ID_1"><br><br></xliff:g>Det kan hende at appen ikke fungerer skikkelig uten denne begrensede tillatelsen. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Finn ut hvordan du gir tilgang</a>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Forespørselen om tillatelse er skjult"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Denne appen ber om flere tillatelser, men du kan ikke gi tillatelser i en strømmeøkt. Gi tillatelsen på telefonen først."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For nødanrop eller -meldinger"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Posisjonen er sendt til nødtjenester"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Denne appen leste posisjonen til enheten din under et anrop eller ved en melding til et nødnummer. Dette kan skje selv om enhetens posisjon er slått av eller appen ikke har posisjonstillatelse. "<a href="https://support.google.com/android/answer/9319337">"Finn ut mer"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Sjekk telefonen"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Fikk ikke kontakt med telefonen din"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vil du prøve på nytt?"</string> </resources> diff --git a/PermissionController/res/values-ne-v35/strings.xml b/PermissionController/res/values-ne-v35/strings.xml new file mode 100644 index 000000000..3d8a7dedf --- /dev/null +++ b/PermissionController/res/values-ne-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"सुरक्षा"</string> +</resources> diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml index 098e9c6b0..b7fb710fd 100644 --- a/PermissionController/res/values-ne/strings.xml +++ b/PermissionController/res/values-ne/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"अनुमतिहरू"</string> <string name="cancel" msgid="8943320028373963831">"रद्द गर्नुहोस्"</string> <string name="back" msgid="6249950659061523680">"पछाडि जानुहोस्"</string> + <string name="dialog_close" msgid="6840699812532384661">"बन्द गर्नुहोस्"</string> <string name="available" msgid="6007778121920339498">"उपलब्ध"</string> <string name="blocked" msgid="9195547604866033708">"ब्लक गरिएको"</string> <string name="on" msgid="280241003226755921">"अन छ"</string> @@ -352,8 +353,8 @@ <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"पहुँचसम्बन्धी <xliff:g id="NUM_SERVICES">%s</xliff:g> एपहरूले तपाईंको यन्त्रमाथि पूर्ण रूपमा पहुँच राख्न सक्छन्"</string> <string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ले तपाईंको स्क्रिन, कारबाही र इनपुट हेर्न, कारबाहीहरू गर्न र डिस्प्ले नियन्त्रण गर्न सक्छ।"</string> <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"यी एपहरूले तपाईंको स्क्रिन, कारबाही र इनपुट हेर्न, कारबाहीहरू सम्पादन गर्न र प्रदर्शन नियन्त्रण गर्न सक्छन्।"</string> - <string name="role_assistant_label" msgid="4727586018198208128">"डिफल्ट डिजिटल सहायक एप"</string> - <string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल सहायक एप"</string> + <string name="role_assistant_label" msgid="4727586018198208128">"डिफल्ट डिजिटल एसिस्टेन्ट एप"</string> + <string name="role_assistant_short_label" msgid="3369003713187703399">"डिजिटल एसिस्टेन्ट एप"</string> <string name="role_assistant_description" msgid="6622458130459922952">"सहायक एपहरूले तपाईंले हेर्दै गर्नुभएको स्क्रिनबाट प्राप्त जानकारीमा आधारित भई तपाईंलाई मद्दत गर्न सक्छन्। केही एपहरूले तपाईंलाई एकीकृत सहायता दिन दुवै लन्चर र आवाज संलग्न इनपुट सेवाहरूलाई समर्थन गर्छन्।"</string> <string name="role_browser_label" msgid="2877796144554070207">"डिफल्ट ब्राउजर एप"</string> <string name="role_browser_short_label" msgid="6745009127123292296">"ब्राउजर"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"कुनै पनि होइन"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डिफल्ट)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"कुनै पनि एप छैन"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"NFC का अन्य सेवाहरू"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"चयन गरिएको"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"चयन गरिएको - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"एपसम्बन्धी विशेष पहुँच"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> डिबग प्रक्रियासम्बन्धी जानकारी अपलोड गर्न चाहन्छ।"</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"डिबग प्रक्रियासम्बन्धी डेटा सेयर गर्ने हो?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"प्रणालीले कुनै समस्या फेला पारेको छ"</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DATE">%2$s</xliff:g> <xliff:g id="TIME">%3$s</xliff:g> मा यो यन्त्रबाट लिएको बगको रिपोर्ट अपलोड गर्ने अनुरोध गर्दै छ। बग रिपोर्टमा प्रयोगकर्ताका नाम, स्ठानसम्बन्धी डेटा, डिभाइसका पहिचानकर्ता र नेटवर्कसम्बन्धी जानकारी जस्ता तपाईंको डिभाइसको व्यक्तिगत जानकारी वा एपले लग गरेको जानकारी समावेश छ। तपाईंलाई यो जानकारी दिँदा फरक पर्दैन जस्तो लाग्ने विश्वसनीय मान्छे वा एपसँग मात्र बग रिपोर्टहरू सेयर गर्नुहोस्। <xliff:g id="APP_NAME_1">%4$s</xliff:g> लाई बग रिपोर्ट अपलोड गर्न दिने हो?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DATE">%2$s</xliff:g> <xliff:g id="TIME">%3$s</xliff:g> मा यो डिभाइसबाट लिएको बगको रिपोर्ट अपलोड गर्ने अनुरोध गर्दै छ। बग रिपोर्टमा प्रयोगकर्ताका नाम, लोकेसन डेटा, डिभाइसका पहिचानकर्ता र नेटवर्कसम्बन्धी जानकारी जस्ता तपाईंको डिभाइसको व्यक्तिगत जानकारी वा एपले लग गरेको जानकारी समावेश छ। तपाईंलाई यो जानकारी दिँदा फरक पर्दैन जस्तो लाग्ने विश्वसनीय मान्छे वा एपसँग मात्र बग रिपोर्टहरू सेयर गर्नुहोस्। <xliff:g id="APP_NAME_1">%4$s</xliff:g> लाई बग रिपोर्ट अपलोड गर्न दिने हो?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"<xliff:g id="APP_NAME">%1$s</xliff:g> को बगसम्बन्धी रिपोर्ट प्रक्रियामा लैजाने क्रममा त्रुटि भयो। त्यस कारण विस्तृत डिबग प्रक्रियासम्बन्धी डेटा आदान प्रदान गर्ने कार्य अस्वीकार गरिएको छ। व्यवधानका लागि क्षमा गर्नुहोला।"</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"अनुमति दिनुहोस्"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"अनुमति नदिनुहोस्"</string> @@ -563,7 +565,7 @@ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"माइक्रोफोन एक्सेस अफ गरिएको छ"</string> <string name="automotive_blocked_location_title" msgid="6047574747593264689">"लोकेसन एक्सेस अफ गरिएको छ"</string> <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"इन्फोटेनमेन्ट एपहरूका लागि"</string> - <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"अनिवार्य रूपमा इन्स्टल गर्नु पर्ने एपहरूका लागि"</string> + <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"आवश्यक एपहरूका लागि"</string> <string name="automotive_required_app_title" msgid="2992168288249988735">"यो एप अनिवार्य रूपमा इन्स्टल गर्नु पर्छ"</string> <string name="automotive_required_app_summary" msgid="6514902316658090465">"तपाईंको कारको उत्पादकले व्यवस्था गरेबमोजिम यो एप अनिवार्य रूपमा इन्स्टल गर्नु पर्छ"</string> <string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"सुरक्षा तथा गोपनीयता"</string> @@ -642,7 +644,7 @@ <string name="permission_rationale_data_sharing_varies_message" msgid="4224469559084489222">"एपको संस्करण, प्रयोगसम्बन्धी जानकारी, तपाईं बसोबास गर्ने क्षेत्र र तपाईंको उमेरका आधारमा जानकारीको व्यवस्थापनसम्बन्धी अभ्यासहरू फरक हुन सक्छन्। "<annotation id="link">"जानकारी सेयर गर्नेसम्बन्धी अभ्यासका बारेमा थप जानकारी"</annotation></string> <string name="permission_rationale_data_sharing_varies_message_without_link" msgid="4912763761399025094">"एपको संस्करण, प्रयोगसम्बन्धी जानकारी, तपाईं बसोबास गर्ने क्षेत्र र तपाईंको उमेरका आधारमा जानकारीको व्यवस्थापनसम्बन्धी अभ्यासहरू फरक हुन सक्छन्।"</string> <string name="permission_rationale_location_settings_title" msgid="7204145004850190953">"तपाईंको लोकेसन डेटा"</string> - <string name="permission_rationale_permission_settings_message" msgid="631286040979660267"><annotation id="link">"गोपनीयतासम्बन्धी सेटिङ"</annotation>"मा गई यो एपलाई दिइएको अनुमति परिवर्तन गर्नुहोस्"</string> + <string name="permission_rationale_permission_settings_message" msgid="631286040979660267"><annotation id="link">"गोपनीयतासम्बन्धी सेटिङ"</annotation>"मा गई यो एपलाई दिइएको एक्सेस परिवर्तन गर्नुहोस्"</string> <string name="permission_rationale_purpose_app_functionality" msgid="8397736681065841405">"एपका सुविधा उपलब्ध गराउने"</string> <string name="permission_rationale_purpose_analytics" msgid="2070800501189620712">"Analytics"</string> <string name="permission_rationale_purpose_developer_communications" msgid="6453047018892062374">"विकासकर्ताबाट जानकारी प्राप्त गर्ने"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"हिजो <xliff:g id="TIME_DATE">%1$s</xliff:g> मा एक्सेस गरिएको"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> मा एक्सेस गरिएको"</string> <string name="test_otp_msg" msgid="7559110574222727550">"तपाईंको एक पटके पासवर्ड 132435 हो"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"प्रतिबन्ध लगाइएका सेटिङ अनलक गर्नुहोस्"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबन्ध लगाइएका सेटिङ"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तपाईंको खाताको सुरक्षार्थ यो सेटिङ हाल उपलब्ध छैन।"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"फोन कल चलिरहेका बेला यो कारबाही गर्न मिल्दैन"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"फोन कल चलिरहेका बेला एपहरूलाई अन्य एप इन्स्टल गर्ने अनुमति दिन मिल्दैन।\n\n स्क्याम गर्ने व्यक्तिले फोन कलका क्रममा प्रायः यस प्रकारको कारबाही गर्न अनुरोध गर्ने भएकाले तपाईंको सुरक्षार्थ यो कारबाही गर्न रोक लगाइएको हो। तपाईंलाई कुनै अपरिचित व्यक्तिले यो कारबाही गर्ने मार्गदर्शन गर्दै छ भने त्यो जालसाजी हुन सक्छ।"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"एपले <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> प्रयोग गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"यो एपले तपाईंको व्यक्तिगत तथा वित्तीय जानकारी जोखिममा पार्न सक्ने खालको संवेदनशील अनुमति मागेको छ।<xliff:g id="ID_1"><br><br></xliff:g>तपाईले उक्त प्रतिबन्धित अनुमति नदिएका खण्डमा यो एपले राम्रोसँग काम नगर्न सक्छ। <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>अनुमति दिने तरिका सिक्नुहोस्</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"यो एपले डिफल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> का रूपमा काम गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ठिक छ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"अनुमतिसम्बन्धी अनुरोध रद्द गरिएको छ"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"यो एपले अतिरिक्त अनुमति मागिरहेको छ तर स्ट्रिमिङ सत्रमा अनुमति दिन मिल्दैन। सर्वप्रथम आफ्नो फोनमा अनुमति दिनुहोस्।"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"आपत्कालीन अवस्थामा यो नम्बरमा कल गर्नुहोस् वा टेक्स्ट म्यासेज पठाउनुहोस्"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"आपत्कालीन सेवाहरूमा लोकेसन पठाइएको छ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"यो एपले आपत्कालीन नम्बरमा कल वा टेक्स्ट म्यासेज गर्ने बेलामा तपाईंको डिभाइसको लोकेसन एक्सेस गरेको छ। यो एपलाई लोकेसन प्रयोग गर्ने अनुमति नदिइएका वा यो डिभाइसको लोकेसन अफ गरिएका अवस्थामा पनि यस्तो हुन सक्छ। "<a href="https://support.google.com/android/answer/9319337">"थप जान्नुहोस्"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"आफ्नो फोनमा हेर्नुहोस्"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"तपाईंको फोनमा कनेक्ट गर्न सकिएन"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"पुनः प्रयास गर्ने हो?"</string> </resources> diff --git a/PermissionController/res/values-night-v33/themes.xml b/PermissionController/res/values-night-v33/themes.xml index 9b6f638a6..31940ede5 100644 --- a/PermissionController/res/values-night-v33/themes.xml +++ b/PermissionController/res/values-night-v33/themes.xml @@ -52,6 +52,8 @@ @style/SecondarySafetyCenterActionButton.Responsive </item> + <item name="scCardSideMargin">@dimen/sc_spacing_large</item> + <item name="textColorScActionButton">@color/sc_primary_action_button_text</item> <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item> diff --git a/PermissionController/res/values-night/themes.xml b/PermissionController/res/values-night/themes.xml index 7ac9b190b..8c6a10db5 100644 --- a/PermissionController/res/values-night/themes.xml +++ b/PermissionController/res/values-night/themes.xml @@ -41,4 +41,6 @@ </style> <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar" /> + + <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.Alert" /> </resources> diff --git a/PermissionController/res/values-nl-v35/strings.xml b/PermissionController/res/values-nl-v35/strings.xml new file mode 100644 index 000000000..f38159c4d --- /dev/null +++ b/PermissionController/res/values-nl-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Beveiliging"</string> +</resources> diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml index 9454dfd1d..1b8015001 100644 --- a/PermissionController/res/values-nl/strings.xml +++ b/PermissionController/res/values-nl/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"rechten"</string> <string name="cancel" msgid="8943320028373963831">"Annuleren"</string> <string name="back" msgid="6249950659061523680">"Terug"</string> + <string name="dialog_close" msgid="6840699812532384661">"Sluiten"</string> <string name="available" msgid="6007778121920339498">"Beschikbaar"</string> <string name="blocked" msgid="9195547604866033708">"Geblokkeerd"</string> <string name="on" msgid="280241003226755921">"Aan"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Geen"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Systeemstandaard)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Geen apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andere NFC-services"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Geselecteerd"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Geselecteerd: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"speciale app-toegang"</string> @@ -449,10 +451,10 @@ <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Ondersteunt geen werkprofielen"</string> <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Opmerking: Als je het apparaat opnieuw opstart en een schermvergrendeling hebt ingesteld, kan deze app pas worden gestart nadat je het apparaat hebt ontgrendeld."</string> <string name="assistant_confirmation_message" msgid="7476540402884416212">"De assistentie-app kan informatie over de gebruikte apps in je systeem lezen, waaronder informatie die zichtbaar is op je scherm of toegankelijk is in de apps."</string> - <string name="incident_report_channel_name" msgid="3144954065936288440">"Foutopsporingsinformatie delen"</string> + <string name="incident_report_channel_name" msgid="3144954065936288440">"Foutopsporingsinformatie delen"</string> <string name="incident_report_notification_title" msgid="4635984625656519773">"Gedetailleerde foutopsporingsinformatie delen?"</string> <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> wil foutopsporingsinformatie uploaden."</string> - <string name="incident_report_dialog_title" msgid="669104389325204095">"Foutopsporingsinformatie delen?"</string> + <string name="incident_report_dialog_title" msgid="669104389325204095">"Foutopsporingsinformatie delen?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Het systeem heeft een probleem gedetecteerd."</string> <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> wil een bugrapport van dit apparaat uploaden dat is gemaakt op <xliff:g id="DATE">%2$s</xliff:g> om <xliff:g id="TIME">%3$s</xliff:g>. Bugrapporten omvatten persoonlijke informatie over je apparaat of gegevens die zijn geregistreerd door apps, zoals gebruikersnamen, locatiegegevens, apparaat-ID\'s en netwerkgegevens. Deel alleen bugrapporten met mensen en apps die je met deze informatie vertrouwt. Wil je <xliff:g id="APP_NAME_1">%4$s</xliff:g> toestaan een bugrapport te uploaden?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Er is een fout opgetreden bij het verwerken van het bugrapport voor <xliff:g id="APP_NAME">%1$s</xliff:g>. Het delen van de gedetailleerde foutopsporingsinformatie is daarom geweigerd. Onze excuses voor de onderbreking."</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Geopend: gisteren om <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Geopend: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> om <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Je eenmalige wachtwoord is 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Beperkte instellingen toestaan"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ter beveiliging is deze instelling op dit moment niet beschikbaar."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Actie niet beschikbaar tijdens een telefoongesprek"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Apps toestaan om andere apps te installeren is niet toegestaan tijdens een telefoongesprek.\n\n Scammers vragen vaak om dit soort acties tijdens telefoongesprekken. Daarom is deze actie geblokkeerd om je te beschermen. Als iemand die je niet kent je aanspoort om deze actie uit te voeren, kan dit een scam zijn."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App heeft geen toegang gekregen tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"De app heeft toegang gevraagd tot een gevoelig recht, waardoor je persoonlijke en financiële informatie risico kunnen lopen.<xliff:g id="ID_1"><br><br></xliff:g>Het is mogelijk dat de app niet goed werkt zonder dit beperkte recht. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Meer informatie over hoe je toegang geeft</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App heeft geen toegang gekregen om de standaard <xliff:g id="ROLE_NAME">%1$s</xliff:g> te worden"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Verzoek om rechten onderdrukt"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Deze app vraagt om extra rechten, maar je kunt geen rechten verlenen tijdens een streamingsessie. Verleen het recht eerst op je telefoon."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Voor noodoproep of -tekstbericht"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Locatie naar de hulpdiensten gestuurd"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Deze app heeft toegang gehad tot de locatie van je apparaat tijdens een gesprek met of tekstbericht naar een alarmnummer. Dit kan ook gebeuren als de app geen locatierechten heeft of als de apparaatlocatie uitstaat. "<a href="https://support.google.com/android/answer/9319337">"Meer informatie"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Check je telefoon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Kan je telefoon niet bereiken"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Opnieuw proberen?"</string> </resources> diff --git a/PermissionController/res/values-or-television/strings.xml b/PermissionController/res/values-or-television/strings.xml index 2859cc59e..181c9640c 100644 --- a/PermissionController/res/values-or-television/strings.xml +++ b/PermissionController/res/values-or-television/strings.xml @@ -20,7 +20,7 @@ <string name="grant_dialog_how_to_change" msgid="997462845048160559">"ଏହାକୁ ଆପଣ ପରେ ଆପ୍ସ ସେଟିଂସ୍ରେ> ବଦଳାଇପାରିବେ"</string> <string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string> <string name="preference_show_system_apps" msgid="4262140518693221093">"ସିଷ୍ଟମ୍ ଆପ୍ସ ଦେଖାନ୍ତୁ"</string> - <string name="app_permissions_decor_title" msgid="7438716722786036814">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string> + <string name="app_permissions_decor_title" msgid="7438716722786036814">"ଆପ ଅନୁମତି"</string> <string name="manage_permissions_decor_title" msgid="4138423885439613577">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string> <string name="permission_apps_decor_title" msgid="2811550489429789828">"<xliff:g id="PERMISSION">%1$s</xliff:g> ଅନୁମତିଗୁଡ଼ିକ"</string> <string name="additional_permissions_decor_title" msgid="5113847982502484225">"ଅତିରିକ୍ତ ଅନୁମତି"</string> diff --git a/PermissionController/res/values-or-v35/strings.xml b/PermissionController/res/values-or-v35/strings.xml new file mode 100644 index 000000000..56dc78f79 --- /dev/null +++ b/PermissionController/res/values-or-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ସୁରକ୍ଷା"</string> +</resources> diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml index 1e76a53f3..621e54e1a 100644 --- a/PermissionController/res/values-or/strings.xml +++ b/PermissionController/res/values-or/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ଅନୁମତିଗୁଡ଼ିକ"</string> <string name="cancel" msgid="8943320028373963831">"ବାତିଲ କରନ୍ତୁ"</string> <string name="back" msgid="6249950659061523680">"ପଛକୁ ଫେରନ୍ତୁ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="available" msgid="6007778121920339498">"ଉପଲବ୍ଧ ଅଛି"</string> <string name="blocked" msgid="9195547604866033708">"ବ୍ଲକ କରାଯାଇଛି"</string> <string name="on" msgid="280241003226755921">"ଚାଲୁ ଅଛି"</string> @@ -59,7 +60,7 @@ <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"ସମସ୍ତ ଫାଇଲର ପରିଚାଳନା ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"ମିଡିଆ ଫାଇଲଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="app_permissions_breadcrumb" msgid="5136969550489411650">"ଆପ୍ସ"</string> - <string name="app_permissions" msgid="3369917736607944781">"ଆପ ଅନୁମତିଗୁଡ଼ିକ"</string> + <string name="app_permissions" msgid="3369917736607944781">"ଆପ ଅନୁମତି"</string> <string name="unused_apps" msgid="2058057455175955094">"ଅବ୍ୟବହୃତ ଆପ୍ସ"</string> <string name="edit_photos_description" msgid="5540108003480078892">"ଏହି ଆପ ପାଇଁ ଚୟନିତ ଫଟୋଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string> <string name="no_unused_apps" msgid="12809387670415295">"କୌଣସି ଅବ୍ୟବହୃତ ଆପ୍ ନାହିଁ"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"କିଛି ଆପ ସେଟ କରାଯାଇନାହିଁ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ସିଷ୍ଟମ୍ ଡିଫଲ୍ଟ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"କୌଣସି ଆପ୍ ନାହିଁ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ଅନ୍ୟ NFC ସେବା"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ଚୟନ କରାଯାଇଛି"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ଚୟନ କରାଯାଇଛି - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ବିଶେଷ ଆପ୍ ଆକ୍ସେସ୍"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ଗତକାଲି <xliff:g id="TIME_DATE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇଛି"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇଛି"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ଆପଣଙ୍କ ଗୋଟିଏ ଥରର ପାସୱାର୍ଡ ହେଉଛି 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂସକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ ଏହି ସେଟିଂ ବର୍ତ୍ତମାନ ଅନୁପଲବ୍ଧ ଅଟେ।"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"କୌଣସି ଫୋନ କଲରେ ଥିବା ସମୟରେ କାର୍ଯ୍ୟ ଉପଲବ୍ଧ ନାହିଁ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"କୌଣସି ଫୋନ କଲ ସମୟରେ ଅନ୍ୟ ଆପ୍ସକୁ ଇନଷ୍ଟଲ କରିବା ପାଇଁ ଆପ୍ସକୁ ଅନୁମତି ଦିଆଯାଏ ନାହିଁ।\n\n ଫୋନ କଲ ବାର୍ତ୍ତାଳାପ ସମୟରେ ସ୍କାମରମାନେ ପ୍ରାୟତଃ ଏହି ପ୍ରକାରର କାର୍ଯ୍ୟ ପାଇଁ ଅନୁରୋଧ କରନ୍ତି, ତେଣୁ ଆପଣଙ୍କୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଏହାକୁ ବ୍ଲକ କରାଯାଏ। ଯଦି ଆପଣଙ୍କୁ ଏହି ପଦକ୍ଷେପ ନେବାକୁ ଆପଣ ଜାଣିନଥିବା ବ୍ୟକ୍ତିଙ୍କ ଦ୍ୱାରା ମାର୍ଗଦର୍ଶନ କରାଯାଉଛି, ତେବେ ଏହା ଏକ ସ୍କାମ ହୋଇପାରେ।"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>କୁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଏବଂ ଆର୍ଥିକ ସୂଚନାକୁ ବିପଦରେ ପକାଇପାରୁଥିବା ଏକ ସମ୍ବେଦନଶୀଳ ଅନୁମତିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପଟି ଅନୁରୋଧ କରିଛି।<xliff:g id="ID_1"><br><br></xliff:g>ଏହା ସମ୍ଭବ ଯେ ଏହି ପ୍ରତିବନ୍ଧିତ ଅନୁମତି ବିନା ଆପ ସଠିକ ଭାବେ କାର୍ଯ୍ୟ କରିବ ନାହିଁ। <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ଆକ୍ସେସକୁ କିପରି ଅନୁମତି ଦେବେ ତାହା ଜାଣନ୍ତୁ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ଡିଫଲ୍ଟ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ହେବା ପାଇଁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ଠିକ ଅଛି"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ଅନୁମତି ଅନୁରୋଧକୁ ବନ୍ଦ କରାଯାଇଛି"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ଏହି ଆପ ଅତିରିକ୍ତ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି, ଏକ ଷ୍ଟ୍ରିମିଂ ସେସନରେ ଅନୁମତି ଦିଆଯାଇପାରିବ ନାହିଁ। ପ୍ରଥମେ ଆପଣଙ୍କ ଫୋନରେ ଅନୁମତି ଦିଅନ୍ତୁ।"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ଜରୁରୀକାଳୀନ କଲ କିମ୍ବା ଟେକ୍ସଟ ପାଇଁ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ଜରୁରୀକାଳୀନ ସେବାଗୁଡ଼ିକୁ ଲୋକେସନ ପଠାଯାଇଛି"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ଏକ ଜରୁରୀକାଳୀନ ନମ୍ବରକୁ କଲ କରାଯିବା କିମ୍ବା ଟେକ୍ସଟ ପଠାଇବା ସମୟରେ ଏହି ଆପ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେସନକୁ ଆକ୍ସେସ କରିଛି। ଆପ ପାଖରେ ଲୋକେସନ ଅନୁମତି ନଥିଲେ କିମ୍ବା ଡିଭାଇସର ଲୋକେସନ ବନ୍ଦ ଥିଲେ ମଧ୍ୟ ଏହା ହୋଇପାରେ। "<a href="https://support.google.com/android/answer/9319337">"ଅଧିକ ଜାଣନ୍ତୁ"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ଆପଣଙ୍କ ଫୋନ ଯାଞ୍ଚ କରନ୍ତୁ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ଆପଣଙ୍କ ଫୋନ ସହ କନେକ୍ଟ କରାଯାଇପାରିଲା ନାହିଁ"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ପୁଣି ଚେଷ୍ଟା କରିବେ?"</string> </resources> diff --git a/PermissionController/res/values-pa-v35/strings.xml b/PermissionController/res/values-pa-v35/strings.xml new file mode 100644 index 000000000..f6cd2ad56 --- /dev/null +++ b/PermissionController/res/values-pa-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ਸੁਰੱਖਿਆ"</string> +</resources> diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml index f00741f53..8984bf0fa 100644 --- a/PermissionController/res/values-pa/strings.xml +++ b/PermissionController/res/values-pa/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ਇਜਾਜ਼ਤਾਂ"</string> <string name="cancel" msgid="8943320028373963831">"ਰੱਦ ਕਰੋ"</string> <string name="back" msgid="6249950659061523680">"ਪਿੱਛੇ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ਬੰਦ ਕਰੋ"</string> <string name="available" msgid="6007778121920339498">"ਉਪਲਬਧ ਹੈ"</string> <string name="blocked" msgid="9195547604866033708">"ਬਲਾਕ ਹੈ"</string> <string name="on" msgid="280241003226755921">"ਚਾਲੂ ਹੈ"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ਕੋਈ ਨਹੀਂ"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ਸਿਸਟਮ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ਕੋਈ ਐਪਾਂ ਨਹੀਂ"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ਹੋਰ NFC ਸੇਵਾਵਾਂ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ਚੁਣੀ ਗਈ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"ਚੁਣੀ ਗਈ - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ਖਾਸ ਐਪ ਪਹੁੰਚ"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"<xliff:g id="TIME_DATE">%1$s</xliff:g> ਵਜੇ ਕੱਲ੍ਹ ਪਹੁੰਚ ਕੀਤੀ ਗਈ"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> ਨੂੰ <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ਵਜੇ ਪਹੁੰਚ ਕੀਤੀ ਗਈ"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ਤੁਹਾਡਾ ਇਕਹਰੀ-ਵਰਤੋਂ ਪਾਸਵਰਡ 132435 ਹੈ"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗਾਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗਾਂ"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਫ਼ਿਲਹਾਲ ਇਹ ਸੈਟਿੰਗ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ਫ਼ੋਨ ਕਾਲ \'ਤੇ ਹੋਣ ਦੌਰਾਨ ਕਾਰਵਾਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ਫ਼ੋਨ ਕਾਲ ਦੌਰਾਨ ਐਪਾਂ ਨੂੰ ਹੋਰ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਜਾਂਦੀ।\n\n ਘਪਲੇਬਾਜ਼ ਅਕਸਰ ਫ਼ੋਨ ਕਾਲ ਦੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਦੌਰਾਨ ਇਸ ਕਿਸਮ ਦੀ ਕਾਰਵਾਈ ਦੀ ਬੇਨਤੀ ਕਰਦੇ ਹਨ, ਇਸ ਲਈ ਤੁਹਾਨੂੰ ਸੁਰੱਖਿਅਤ ਰੱਖਣ ਲਈ ਇਸਨੂੰ ਬਲਾਕ ਕੀਤਾ ਹੁੰਦਾ ਹੈ। ਜੇ ਤੁਹਾਨੂੰ ਕਿਸੇ ਅਨਜਾਣ ਵਿਅਕਤੀ ਵੱਲੋਂ ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਸੇਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ ਇੱਕ ਘਪਲਾ ਹੋਵੇ।"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ਐਪ ਨੂੰ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ਐਪ ਨੇ ਸੰਵੇਦਨਸ਼ੀਲ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਨਿੱਜੀ ਅਤੇ ਵਿੱਤੀ ਜਾਣਕਾਰੀ ਜੋਖਮ ਵਿੱਚ ਪੈ ਸਕਦੀ ਹੈ।<xliff:g id="ID_1"><br><br></xliff:g>ਇਹ ਸੰਭਵ ਹੈ, ਕਿ ਐਪ ਇਸ ਪ੍ਰਤਿਬੰਧਿਤ ਇਜਾਜ਼ਤ ਤੋਂ ਬਿਨਾਂ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣ ਦੇ ਤਰੀਕੇ ਬਾਰੇ ਜਾਣੋ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ਐਪ ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ਠੀਕ ਹੈ"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਬੇਨਤੀ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ਇਹ ਐਪ ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ ਦੀ ਬੇਨਤੀ ਕਰ ਰਹੀ ਹੈ, ਪਰ ਸਟ੍ਰੀਮਿੰਗ ਸੈਸ਼ਨ ਵਿੱਚ ਇਜਾਜ਼ਤਾਂ ਨਹੀਂ ਦਿੱਤੀਆਂ ਜਾ ਸਕਦੀਆਂ। ਪਹਿਲਾਂ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਇਜਾਜ਼ਤ ਦਿਓ।"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ਐਮਰਜੈਂਸੀ ਕਾਲ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹੇ ਲਈ"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ਐਮਰਜੈਂਸੀ ਸੇਵਾਵਾਂ ਨੂੰ ਭੇਜੀ ਗਈ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ਇਸ ਐਪ ਵੱਲੋਂ ਐਮਰਜੈਂਸੀ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰਨ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹਾ ਭੇਜਣ ਦੌਰਾਨ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕੀਤੀ ਗਈ। ਇਹ ਉਦੋਂ ਵੀ ਹੋ ਸਕਦਾ ਹੈ, ਜਦੋਂ ਐਪ ਕੋਲ ਟਿਕਾਣੇ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੁੰਦੀ ਹੈ ਜਾਂ ਡੀਵਾਈਸ ਦੀ ਟਿਕਾਣਾ ਸੁਵਿਧਾ ਬੰਦ ਹੁੰਦੀ ਹੈ। "<a href="https://support.google.com/android/answer/9319337">"ਹੋਰ ਜਾਣੋ"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ਆਪਣੇ ਫ਼ੋਨ ਦੀ ਜਾਂਚ ਕਰੋ"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ਕੀ ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰਨੀ ਹੈ?"</string> </resources> diff --git a/PermissionController/res/values-pl-v35/strings.xml b/PermissionController/res/values-pl-v35/strings.xml new file mode 100644 index 000000000..441703f88 --- /dev/null +++ b/PermissionController/res/values-pl-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Bezpieczeństwo"</string> +</resources> diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml index 3e402cb98..491216726 100644 --- a/PermissionController/res/values-pl/strings.xml +++ b/PermissionController/res/values-pl/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"uprawnienia"</string> <string name="cancel" msgid="8943320028373963831">"Anuluj"</string> <string name="back" msgid="6249950659061523680">"Wstecz"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zamknij"</string> <string name="available" msgid="6007778121920339498">"Odblokowany"</string> <string name="blocked" msgid="9195547604866033708">"Zablokowany"</string> <string name="on" msgid="280241003226755921">"Włączona"</string> @@ -249,13 +250,13 @@ <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Aktualnie odmowa / ostatni dostęp: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Nigdy nie użyto"</string> <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Odmowa / nigdy nie użyto"</string> - <string name="allowed_header" msgid="7769277978004790414">"Mają dostęp"</string> + <string name="allowed_header" msgid="7769277978004790414">"Ma dostęp"</string> <string name="allowed_always_header" msgid="6455903312589013545">"Mają ciągły dostęp"</string> <string name="allowed_foreground_header" msgid="6845655788447833353">"Mają dostęp tylko podczas używania"</string> <string name="allowed_storage_scoped" msgid="5383645873719086975">"Zezwolono na dostęp tylko do multimediów"</string> <string name="allowed_storage_full" msgid="5356699280625693530">"Zezwolono na zarządzanie wszystkimi plikami"</string> <string name="ask_header" msgid="2633816846459944376">"Zawsze pytaj"</string> - <string name="denied_header" msgid="903209608358177654">"Nie mają dostępu"</string> + <string name="denied_header" msgid="903209608358177654">"Nie ma dostępu"</string> <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> na tym urządzeniu: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Brak"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Domyślna aplikacja systemowa)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Brak aplikacji"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Inne usługi NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Wybrana"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Wybrana – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"aplikacje ze specjalnym dostępem"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Ostatni dostęp wczoraj, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ostatni dostęp <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Twoje hasło jednorazowe to 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Zezwól na ustawienia z ograniczonym dostępem"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ustawienie z ograniczonym dostępem"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ze względów bezpieczeństwa to ustawienie jest obecnie niedostępne."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Działanie niedostępne podczas rozmowy telefonicznej"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Podczas rozmowy telefonicznej nie można zezwolić aplikacjom na instalowanie innych aplikacji.\n\n Oszuści często proszą o takie działania podczas rozmów telefonicznych, dlatego blokujemy je, aby Cię chronić. Jeśli ktoś, kogo nie znasz, prosi Cię o podjęcie tego działania, może to być oszustwo."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacja nie otrzymała dostępu do uprawnień <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacja wymaga dostępu do uprawnień newralgicznych, co może zagrażać Twoim danym osobowym i informacjom finansowym.<xliff:g id="ID_1"><br><br></xliff:g>Możliwe, że bez tego uprawnienia z ograniczeniami aplikacja nie będzie działać poprawnie. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Jak zezwolić na dostęp</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacja nie otrzymała dostępu jako domyślna <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Prośba o uprawnienia została zablokowana"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ta aplikacja prosi o dodatkowe uprawnienia, ale nie można ich przyznać w trakcie sesji strumieniowania. Najpierw przyznaj te uprawnienia na telefonie."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Na potrzeby połączenia lub SMS-a alarmowego"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokalizacja została wysłana do służb ratunkowych"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ta aplikacja uzyskała dostęp do lokalizacji Twojego urządzenia podczas połączenia z numerem alarmowym lub wysyłania SMS-a pod ten numer. Może się tak zdarzyć, nawet jeśli aplikacja nie ma uprawnień do lokalizacji lub gdy lokalizacja jest wyłączona na urządzeniu. "<a href="https://support.google.com/android/answer/9319337">"Więcej informacji"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Sprawdź telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nie można połączyć się z telefonem"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Spróbować ponownie?"</string> </resources> diff --git a/PermissionController/res/values-pt-rBR-v34/strings.xml b/PermissionController/res/values-pt-rBR-v34/strings.xml index 78bebb7a5..28380d50b 100644 --- a/PermissionController/res/values-pt-rBR-v34/strings.xml +++ b/PermissionController/res/values-pt-rBR-v34/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança e privacidade"</string> + <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança física e privacidade"</string> <string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Controles"</string> <string name="health_connect_title" msgid="2132233890867430855">"Conexão Saúde"</string> <string name="health_connect_summary" msgid="815473513776882296">"Gerenciar o acesso de apps aos dados de saúde"</string> diff --git a/PermissionController/res/values-pt-rBR-v35/strings.xml b/PermissionController/res/values-pt-rBR-v35/strings.xml new file mode 100644 index 000000000..96603658a --- /dev/null +++ b/PermissionController/res/values-pt-rBR-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Segurança"</string> +</resources> diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml index 3bb55bfbf..e49acd0c8 100644 --- a/PermissionController/res/values-pt-rBR/strings.xml +++ b/PermissionController/res/values-pt-rBR/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissões"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Voltar"</string> + <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string> <string name="available" msgid="6007778121920339498">"Disponível"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Ativado"</string> @@ -361,7 +362,7 @@ <string name="role_browser_request_title" msgid="2895200507835937192">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como navegador padrão?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"Nenhuma permissão necessária"</string> <string name="role_dialer_label" msgid="1100224146343237968">"App de telefone padrão"</string> - <string name="role_dialer_short_label" msgid="7186888549465352489">"App de Telefone"</string> + <string name="role_dialer_short_label" msgid="7186888549465352489">"App de telefone"</string> <string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem fazer e receber chamadas no seu dispositivo."</string> <string name="role_dialer_request_title" msgid="5959618560705912058">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de telefone padrão?"</string> <string name="role_dialer_request_description" msgid="6288839625724909320">"Este app poderá acessar contatos, câmera, microfone, telefone e SMS"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Padrão do sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nenhum app"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Outros serviços de NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selecionado"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selecionado: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Acesso especial para apps"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acesso: ontem, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Sua senha única é 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configurações restritas"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação indisponível durante uma ligação"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é possível permitir que apps instalem outros apps durante uma ligação.\n\n Golpistas costumam pedir esse tipo de ação durante ligações, então ela fica bloqueada para sua proteção. Se você está recebendo instruções de uma pessoa desconhecida para realizar essa ação, isso pode ser um golpe."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1"><br><br></xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Aprenda a conceder acesso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitação de permissão suprimida"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esse app está solicitando permissões extras, mas elas não podem ser concedidas em uma sessão de streaming. Dê permissão pelo smartphone primeiro."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para uma chamada ou mensagem de texto de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"O app acessou a localização do seu dispositivo durante uma ligação ou mensagem de texto para um número de emergência. Isso pode acontecer mesmo quando o app não tem permissão de localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Confira seu smartphone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Não foi possível se conectar ao smartphone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Tentar de novo?"</string> </resources> diff --git a/PermissionController/res/values-pt-rPT-v35/strings.xml b/PermissionController/res/values-pt-rPT-v35/strings.xml new file mode 100644 index 000000000..96603658a --- /dev/null +++ b/PermissionController/res/values-pt-rPT-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Segurança"</string> +</resources> diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml index efccb407e..9989caf2e 100644 --- a/PermissionController/res/values-pt-rPT/strings.xml +++ b/PermissionController/res/values-pt-rPT/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"autorizações"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Anterior"</string> + <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string> <string name="available" msgid="6007778121920339498">"Disponível"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Ativado"</string> @@ -209,7 +210,7 @@ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gerir app se não for usada"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Remover autorizações, eliminar ficheiros temporários e parar notificações"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove autorizações, elimina ficheiros temporários, interrompe notificações e arquiva a app"</string> - <string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger os seus dados, as autorizações desta app serão removidas se a mesma não for utilizada durante alguns meses."</string> + <string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger os seus dados, as autorizações desta app serão removidas se a mesma não for usada durante alguns meses."</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger os seus dados, se a app não for utilizada há alguns meses, serão removidas as seguintes autorizações: <xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger os seus dados, foram removidas as autorizações para as apps que não utiliza há alguns meses."</string> <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"Se pretender permitir novamente as autorizações, abra a app."</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Predefinição do sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Sem apps"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Outros serviços NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selecionada"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selecionada – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"acesso especial a apps"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Acedido ontem à(s) <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Acedido a <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à(s) <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"A sua palavra-passe de utilização única é 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir definições restritas"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Definição restrita"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, esta definição está indisponível atualmente."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação não disponível durante uma chamada"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é permitido permitir que as apps instalem outras apps durante uma chamada telefónica.\n\n Os autores de esquemas pedem frequentemente este tipo de ação durante as conversas telefónicas, por isso, a opção é bloqueada para sua proteção. Se alguém que não conhece lhe pedir para realizar esta ação, pode tratar-se de um esquema."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O acesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> foi negado à app"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A app pediu acesso a uma autorização confidencial que pode pôr em risco as suas informações pessoais e financeiras.<xliff:g id="ID_1"><br><br></xliff:g>É possível que a app não funcione corretamente sem esta autorização restrita. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Saiba como permitir o acesso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O acesso à função predefinida <xliff:g id="ROLE_NAME">%1$s</xliff:g> foi negado à app"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Pedido de autorização suprimido"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta app está a pedir autorizações adicionais, mas não é possível conceder autorizações numa sessão de streaming. Comece por conceder a autorização no seu telemóvel."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para chamada ou mensagem de texto de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta app acedeu à localização do seu dispositivo durante uma chamada ou o envio de uma mensagem de texto para um número de emergência. Isto pode acontecer mesmo quando a app não tem autorização de acesso à localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Verifique o seu telemóvel"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Não foi possível ligar ao telemóvel"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Tentar novamente?"</string> </resources> diff --git a/PermissionController/res/values-pt-v34/strings.xml b/PermissionController/res/values-pt-v34/strings.xml index 78bebb7a5..28380d50b 100644 --- a/PermissionController/res/values-pt-v34/strings.xml +++ b/PermissionController/res/values-pt-v34/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança e privacidade"</string> + <string name="security_privacy_brand_name" msgid="7303621734258440812">"Segurança física e privacidade"</string> <string name="privacy_subpage_controls_header" msgid="4152396976713749322">"Controles"</string> <string name="health_connect_title" msgid="2132233890867430855">"Conexão Saúde"</string> <string name="health_connect_summary" msgid="815473513776882296">"Gerenciar o acesso de apps aos dados de saúde"</string> diff --git a/PermissionController/res/values-pt-v35/strings.xml b/PermissionController/res/values-pt-v35/strings.xml new file mode 100644 index 000000000..96603658a --- /dev/null +++ b/PermissionController/res/values-pt-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Segurança"</string> +</resources> diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml index 3bb55bfbf..e49acd0c8 100644 --- a/PermissionController/res/values-pt/strings.xml +++ b/PermissionController/res/values-pt/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permissões"</string> <string name="cancel" msgid="8943320028373963831">"Cancelar"</string> <string name="back" msgid="6249950659061523680">"Voltar"</string> + <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string> <string name="available" msgid="6007778121920339498">"Disponível"</string> <string name="blocked" msgid="9195547604866033708">"Bloqueado"</string> <string name="on" msgid="280241003226755921">"Ativado"</string> @@ -361,7 +362,7 @@ <string name="role_browser_request_title" msgid="2895200507835937192">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como navegador padrão?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"Nenhuma permissão necessária"</string> <string name="role_dialer_label" msgid="1100224146343237968">"App de telefone padrão"</string> - <string name="role_dialer_short_label" msgid="7186888549465352489">"App de Telefone"</string> + <string name="role_dialer_short_label" msgid="7186888549465352489">"App de telefone"</string> <string name="role_dialer_description" msgid="8768708633696539612">"Apps que permitem fazer e receber chamadas no seu dispositivo."</string> <string name="role_dialer_request_title" msgid="5959618560705912058">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de telefone padrão?"</string> <string name="role_dialer_request_description" msgid="6288839625724909320">"Este app poderá acessar contatos, câmera, microfone, telefone e SMS"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Padrão do sistema)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nenhum app"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Outros serviços de NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selecionado"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selecionado: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Acesso especial para apps"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acesso: ontem, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Sua senha única é 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permitir configurações restritas"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Ação indisponível durante uma ligação"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Não é possível permitir que apps instalem outros apps durante uma ligação.\n\n Golpistas costumam pedir esse tipo de ação durante ligações, então ela fica bloqueada para sua proteção. Se você está recebendo instruções de uma pessoa desconhecida para realizar essa ação, isso pode ser um golpe."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1"><br><br></xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Aprenda a conceder acesso</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitação de permissão suprimida"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esse app está solicitando permissões extras, mas elas não podem ser concedidas em uma sessão de streaming. Dê permissão pelo smartphone primeiro."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para uma chamada ou mensagem de texto de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"O app acessou a localização do seu dispositivo durante uma ligação ou mensagem de texto para um número de emergência. Isso pode acontecer mesmo quando o app não tem permissão de localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Confira seu smartphone"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Não foi possível se conectar ao smartphone"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Tentar de novo?"</string> </resources> diff --git a/PermissionController/res/values-ro-v35/strings.xml b/PermissionController/res/values-ro-v35/strings.xml new file mode 100644 index 000000000..856c75c82 --- /dev/null +++ b/PermissionController/res/values-ro-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Securitate"</string> +</resources> diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml index 3fe40231d..d75d6c6de 100644 --- a/PermissionController/res/values-ro/strings.xml +++ b/PermissionController/res/values-ro/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"permisiuni"</string> <string name="cancel" msgid="8943320028373963831">"Anulează"</string> <string name="back" msgid="6249950659061523680">"Înapoi"</string> + <string name="dialog_close" msgid="6840699812532384661">"Închide"</string> <string name="available" msgid="6007778121920339498">"Disponibil"</string> <string name="blocked" msgid="9195547604866033708">"Blocat"</string> <string name="on" msgid="280241003226755921">"Activat"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Niciuna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Prestabilită de sistem)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nicio aplicație"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Alte servicii NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Selectată"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selectat - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"acces special pentru aplicații"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> dorește să încarce informațiile despre remedierea erorilor."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Trimiți datele despre remedierea erorilor?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistemul a detectat o problemă."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> solicită încărcarea unui raport de erori de pe acest dispozitiv creat în data de <xliff:g id="DATE">%2$s</xliff:g> la <xliff:g id="TIME">%3$s</xliff:g>. Rapoartele de erori conțin informații cu caracter personal despre dispozitiv sau înregistrate de aplicații, de exemplu: nume de utilizator, date privind locațiile, identificatori ai dispozitivului și informații despre rețea. Trimite rapoarte de erori doar persoanelor și aplicațiilor de încredere. Permiți ca <xliff:g id="APP_NAME_1">%4$s</xliff:g> să încarce un raport de erori?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> solicită încărcarea unui raport de eroare de pe acest dispozitiv creat în data de <xliff:g id="DATE">%2$s</xliff:g> la <xliff:g id="TIME">%3$s</xliff:g>. Rapoartele de erori conțin informații cu caracter personal despre dispozitiv sau înregistrate de aplicații, de exemplu: nume de utilizator, date privind locațiile, identificatori ai dispozitivului și informații despre rețea. Trimite rapoarte de erori doar persoanelor și aplicațiilor de încredere. Permiți ca <xliff:g id="APP_NAME_1">%4$s</xliff:g> să încarce un raport de eroare?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"A apărut o eroare la procesarea raportului de eroare pentru <xliff:g id="APP_NAME">%1$s</xliff:g>. Astfel, accesul la datele detaliate de remedierea erorilor a fost refuzat. Ne cerem scuze pentru întrerupere."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Permite"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Refuz"</string> @@ -512,7 +514,7 @@ <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aplicația dorește să înregistreze conținut audio permanent, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string> <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Permiți aplicației <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să-ți acceseze activitatea fizică?"</string> <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să acceseze activitatea ta fizică de pe <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_camera" msgid="5123097035410002594">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să fotografieze și să înregistreze video?"</string> + <string name="permgrouprequest_camera" msgid="5123097035410002594">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să fotografieze și să înregistreze videoclipuri?"</string> <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să facă fotografii și să înregistreze videoclipuri pe <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplicația va putea să fotografieze și să înregistreze videoclipuri doar când o folosești"</string> <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Permiți ca <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să facă fotografii și să înregistreze videoclipuri?"</string> @@ -550,9 +552,7 @@ <string name="privdash_label_none" msgid="5991866260360484858">"Fără"</string> <string name="privdash_label_24h" msgid="1512532123865375319">"Ultimele\n24 de ore"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"Ultimele\nșapte zile"</string> - <!-- String.format failed for translation --> - <!-- no translation found for privdash_usage_percent (6893824766124414127) --> - <skip /> + <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> %%"</string> <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> este protejată de Android. Întrucât datele tale sunt procesate pe dispozitiv, folosirea permisiunilor de către aplicație nu apare în bara de stare sau în tabloul de bord de confidențialitate."</string> <string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> este protejată de Android. Întrucât datele tale sunt procesate pe dispozitiv, folosirea permisiunilor de către aplicație nu apare în tabloul de bord de confidențialitate."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"Camera dispozitivului este blocată"</string> @@ -671,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Data accesării: ieri, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Data accesării: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Parola ta unică este 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Permite setările restricționate"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setare restricționată"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pentru securitatea ta, setarea este momentan indisponibilă."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Acțiune indisponibilă în timpul apelului telefonic"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nu este permisă instalarea altor aplicații în timpul unui apel telefonic.\n\n Escrocii solicită adesea acest tip de acțiune în timpul conversațiilor telefonice, astfel că opțiunea este blocată pentru a te proteja. Dacă o persoană necunoscută te îndrumă să faci această acțiune, este posibil să fie o escrocherie."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Accesul aplicației la <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a fost refuzat"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplicația a solicitat acces la o permisiune de accesare a informațiilor sensibile care îți poate pune în pericol informațiile financiare și cu caracter personal.<xliff:g id="ID_1"><br><br></xliff:g>Este posibil ca aplicația să nu funcționeze corect fără această permisiune restricționată. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Află cum să permiți accesul</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Accesul aplicației pentru a deveni <xliff:g id="ROLE_NAME">%1$s</xliff:g> prestabilită a fost refuzat"</string> @@ -683,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitarea de permisiune s-a suprimat"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplicația solicită permisiuni suplimentare, dar acestea nu pot fi acordate într-o sesiune de streaming. Acordă permisiunea întâi pe telefon."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pentru apel sau mesaj de urgență"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Locație trimisă la serviciile de urgență"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Această aplicație a accesat locația dispozitivului când ai inițiat un apel sau ai trimis un mesaj către un număr de urgență. Se poate întâmpla chiar dacă aplicația nu are permisiune pentru locație sau locația dispozitivului este dezactivată."<a href="https://support.google.com/android/answer/9319337">"Află mai mult"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Verifică-ți telefonul"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Nu s-a putut accesa telefonul"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Încerci din nou?"</string> </resources> diff --git a/PermissionController/res/values-ru-v35/strings.xml b/PermissionController/res/values-ru-v35/strings.xml new file mode 100644 index 000000000..c6fa35d68 --- /dev/null +++ b/PermissionController/res/values-ru-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Безопасность"</string> +</resources> diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml index 8fb4530f3..8d99ca6aa 100644 --- a/PermissionController/res/values-ru/strings.xml +++ b/PermissionController/res/values-ru/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"разрешения"</string> <string name="cancel" msgid="8943320028373963831">"Отмена"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Закрыть"</string> <string name="available" msgid="6007778121920339498">"Доступно"</string> <string name="blocked" msgid="9195547604866033708">"Заблокировано"</string> <string name="on" msgid="280241003226755921">"Включено"</string> @@ -163,7 +164,7 @@ <string name="permission_usage_bar_chart_title_last_minute" msgid="820450867183487607">"Разрешения, использованные за последнюю минуту"</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_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> <string name="app_permission_usage_remove_filter" msgid="2926157607436428207">"Удалить фильтр"</string> @@ -223,7 +224,7 @@ <string name="auto_revoked_app_summary_two" msgid="1910545340763709389">"Отозваны разрешения \"<xliff:g id="PERMISSION_NAME_0">%1$s</xliff:g>\" и \"<xliff:g id="PERMISSION_NAME_1">%2$s</xliff:g>\"."</string> <string name="auto_revoked_app_summary_many" msgid="5930976230827378798">"Отозвано разрешение \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\" и ещё <xliff:g id="NUMBER">%2$s</xliff:g>."</string> <string name="unused_apps_page_title" msgid="6986983535677572559">"Неиспользуемые приложения"</string> - <string name="unused_apps_page_summary" msgid="1867593913217272155">"Если вы не пользовались приложением несколько месяцев:\n\n• разрешения будут отозваны для защиты ваших данных;\n• уведомления будут отключены для экономии заряда батареи;\n• временные файлы будут удалены, чтобы освободить место.\n\nЧтобы заново предоставить разрешения и включить уведомления, откройте приложение."</string> + <string name="unused_apps_page_summary" msgid="1867593913217272155">"Если вы не пользовались приложением несколько месяцев:\n\n• Разрешения будут отозваны, чтобы защитить ваши данные\n• Уведомления будут отключены, чтобы экономить заряд батареи\n• Временные файлы будут удалены, чтобы освободить место\n\nЧтобы заново предоставить разрешения и включить уведомления, откройте приложение."</string> <string name="unused_apps_page_tv_summary" msgid="2624911608663778308">"Если вы не пользовались приложением в течение месяца:\n\n• разрешения будут отключены для защиты ваших данных;\n• временные файлы будут удалены, чтобы освободить место.\n\nЧтобы заново предоставить разрешения, откройте приложение."</string> <string name="last_opened_category_title" msgid="8796557894614236128">"{count,plural, =1{Использовались более # месяца назад}one{Использовались более # месяца назад}few{Использовались более # месяцев назад}many{Использовались более # месяцев назад}other{Использовались более # месяца назад}}"</string> <string name="last_opened_summary" msgid="5248984030024968808">"Вы открывали это приложение <xliff:g id="DATE">%s</xliff:g>"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Нет"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(по умолчанию)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Приложений нет"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Другие сервисы NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Выбрано"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Выбрано: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"специальный доступ для приложений"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение на загрузку данных об отладке."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Отправить данные об отладке?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"В системе обнаружена проблема"</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" запрашивает ваше согласие на загрузку с этого устройства отчета об ошибке, созданного <xliff:g id="DATE">%2$s</xliff:g> в <xliff:g id="TIME">%3$s</xliff:g>. Отчет может содержать персональную информацию с устройства или из установленных приложений, например имена пользователей, сведения о местоположении, идентификаторы устройства и данные сети. Отправляйте отчеты об ошибке только тем приложениям и пользователям, которым доверяете. Разрешить приложению \"<xliff:g id="APP_NAME_1">%4$s</xliff:g>\" загрузить отчет об ошибке?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" запрашивает разрешение загрузить с этого устройства отчет об ошибке, созданный <xliff:g id="DATE">%2$s</xliff:g> в <xliff:g id="TIME">%3$s</xliff:g>. Отчет может содержать персональную информацию об устройстве или сведения, сохраненные приложениями, например имена пользователей, геоданные, идентификаторы устройства и информацию о сетях. Отправляйте отчеты об ошибке только тем приложениям и пользователям, которым доверяете. Разрешить приложению \"<xliff:g id="APP_NAME_1">%4$s</xliff:g>\" загрузить отчет об ошибке?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Не удалось обработать информацию об ошибке в приложении \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". Данные об отладке не отправлены. Приносим извинения за неудобства."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Разрешить"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Запретить"</string> @@ -550,9 +552,7 @@ <string name="privdash_label_none" msgid="5991866260360484858">"Нет"</string> <string name="privdash_label_24h" msgid="1512532123865375319">"Последние\n24 часа"</string> <string name="privdash_label_7d" msgid="5645301995348656931">"Последние\n7 дней"</string> - <!-- String.format failed for translation --> - <!-- no translation found for privdash_usage_percent (6893824766124414127) --> - <skip /> + <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: <xliff:g id="PERCENT">%2$d</xliff:g> %%"</string> <string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" защищено Android. Поскольку ваши данные обрабатываются на устройстве, разрешения, используемые приложением, не показываются в строке состояния на панели управления разрешениями."</string> <string name="exempt_info_label" msgid="6286190981253476699">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" защищено Android. Поскольку ваши данные обрабатываются на устройстве, разрешения, используемые приложением, не показываются на панели управления разрешениями."</string> <string name="blocked_camera_title" msgid="1128510551791284384">"Камера устройства заблокирована"</string> @@ -580,7 +580,7 @@ <string name="security_settings" msgid="3808106921175271317">"Настройки безопасности"</string> <string name="sensor_permissions_qs" msgid="1022267900031317472">"Разрешения"</string> <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Защита и конфиденциальность"</string> - <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Проверьте статус."</string> + <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Проверьте состояние"</string> <string name="privacy_controls_qs" msgid="5780144882040591169">"Ваши настройки конфиденциальности"</string> <string name="security_settings_button_label_qs" msgid="8280343822465962330">"Другие настройки"</string> <string name="camera_toggle_label_qs" msgid="3880261453066157285">"Доступ к камере"</string> @@ -655,15 +655,15 @@ <string name="app_permission_rationale_message" msgid="8511466916077100713">"Безопасность данных"</string> <string name="app_location_permission_rationale_title" msgid="925420340572401350">"Возможна передача геоданных"</string> <string name="app_location_permission_rationale_subtitle" msgid="6986985722752868692">"Это приложение уведомило, что оно может передавать данные о вашем местоположении третьим лицам."</string> - <string name="data_sharing_updates_title" msgid="7996933386875213859">"Обновления в передаче данных о местоположении"</string> - <string name="data_sharing_updates_summary" msgid="764113985772233889">"Проверить приложения, которые изменили подход к передаче данных о вашем местоположении"</string> - <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"Эти приложения изменили подход к передаче данных о вашем местоположении. Возможно, такие сведения ранее не передавались или теперь передаются в рекламных или маркетинговых целях."</string> - <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Разработчики этих приложений предоставили магазину информацию о своем подходе к передаче данных. Эти сведения могут обновляться.\n\nПодход к передаче данных может зависеть от версии приложения, способа его использования, а также от вашего региона и возраста."</string> + <string name="data_sharing_updates_title" msgid="7996933386875213859">"Изменения в передаче геоданных"</string> + <string name="data_sharing_updates_summary" msgid="764113985772233889">"Проверить приложения, в которых изменились разрешения на передачу геоданных"</string> + <string name="data_sharing_updates_subtitle" msgid="6311537708950632329">"В этих приложениях изменились разрешения на передачу геоданных. Возможно, эти данные ранее не передавались вообще или теперь передаются в рекламных или маркетинговых целях."</string> + <string name="data_sharing_updates_footer_message" msgid="1582711655172892107">"Разработчики этих приложений указали в магазине приложений, как их приложения передают данные. Эта информация может изменяться.\n\nРазрешения на передачу данных могут зависеть от версии приложения, способа его использования, а также от вашего региона и возраста."</string> <string name="learn_about_data_sharing" msgid="4200480587079488045">"Сведения о передаче данных"</string> <string name="shares_location_with_third_parties" msgid="2278051743742057767">"Теперь данные о вашем местоположении передаются третьим лицам."</string> <string name="shares_location_with_third_parties_for_advertising" msgid="1918588064014480513">"Теперь данные о вашем местоположении передаются третьим лицам в рекламных или маркетинговых целях."</string> <string name="updated_in_last_days" msgid="8371811947153042322">"{count,plural, =0{Обновлено за последний день}=1{Обновлено за последний день}one{Обновлено за последний # день}few{Обновлено за последние # дня}many{Обновлено за последние # дней}other{Обновлено за последние # дня}}"</string> - <string name="no_updates_at_this_time" msgid="9031085635689982935">"Обновлений пока нет"</string> + <string name="no_updates_at_this_time" msgid="9031085635689982935">"Изменений нет"</string> <string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Обновление сведений о передаче данных"</string> <string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Некоторые приложения изменили подход к передаче данных о вашем местоположении."</string> <string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Настройки"</string> @@ -671,16 +671,25 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Последний доступ вчера: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Последний доступ: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ваш одноразовый код: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Разрешить доступ к настройкам"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Настройки с ограниченным доступом"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"В целях безопасности эти настройки пока недоступны."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Действие недоступно во время звонка."</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Во время звонка нельзя давать приложениям разрешение на установку приложений.\n\n Об этом часто просят телефонные мошенники, поэтому мы заблокировали эту функцию, чтобы защитить вас. Если незнакомый человек предлагает вам предоставить такое разрешение, возможно, это мошенник."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Для приложения заблокировано разрешение \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложение запрашивает разрешение на доступ к конфиденциальной информации. Если вы предоставите его, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1"><br><br></xliff:g>Без такого разрешения приложение может работать неправильно. Узнайте, <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>как предоставить доступ к данным</a>."</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Запрещено использовать приложение в качестве стандартного в категории \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\""</string> - <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложение запрашивает разрешения на доступ к конфиденциальной информации. Если вы предоставите их, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1"><br><br></xliff:g>Без таких разрешений приложение может работать неправильно. Узнайте, <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>как предоставить доступ к данным</a>."</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Запрос на использование в качестве приложения по умолчанию (<xliff:g id="ROLE_NAME">%1$s</xliff:g>) отклонен"</string> + <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложение запросило разрешения на доступ к конфиденциальной информации. Такие разрешения могут поставить ваши личные и финансовые данные под угрозу.<xliff:g id="ID_1"><br><br></xliff:g>Без них приложение может работать неправильно. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Как предоставить доступ к данным.</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Доступ запрещен"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Если вы предоставите это разрешение, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1"><br><br></xliff:g>Без него приложение может работать неправильно. Узнайте, <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>как предоставить доступ к данным</a>."</string> <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Подробнее"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запрос разрешений заблокирован"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Это приложение запрашивает дополнительные разрешения, которые невозможно предоставить во время трансляции на устройство. Сначала откройте доступ на телефоне."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Для экстренного вызова или сообщения"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Местоположение отправлено экстренным службам"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Это приложение получало доступ к местоположению устройства во время звонка или отправки сообщения на номер экстренной службы. Такое возможно, даже если у приложения нет разрешения на определение местоположения или геолокация отключена на устройстве. "<a href="https://support.google.com/android/answer/9319337">"Подробнее…"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Проверьте телефон"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Нет связи с телефоном."</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Повторить попытку?"</string> </resources> diff --git a/PermissionController/res/values-si-v35/strings.xml b/PermissionController/res/values-si-v35/strings.xml new file mode 100644 index 000000000..dd5c408ae --- /dev/null +++ b/PermissionController/res/values-si-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ආරක්ෂාව"</string> +</resources> diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml index d7458173f..4f0a78c39 100644 --- a/PermissionController/res/values-si/strings.xml +++ b/PermissionController/res/values-si/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"අවසර"</string> <string name="cancel" msgid="8943320028373963831">"අවලංගු කරන්න"</string> <string name="back" msgid="6249950659061523680">"ආපසු යන්න"</string> + <string name="dialog_close" msgid="6840699812532384661">"වසන්න"</string> <string name="available" msgid="6007778121920339498">"තිබේ"</string> <string name="blocked" msgid="9195547604866033708">"අවහිරයි"</string> <string name="on" msgid="280241003226755921">"සක්රීය"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"කිසිවක් නැත"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(පද්ධතිය පෙරනිමි)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"යෙදුම් නොමැත"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"වෙනත් NFC සේවා"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"තේරිණි"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"තේරිණි - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"විශේෂ යෙදුම් ප්රවේශය"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ප්රවේශ වූයේ ඊයේ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ප්රවේශ වූයේ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ඔබේ එක් වරක මුරපදය 132435 වේ"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"සීමා කළ සැකසීම්වලට ඉඩ දෙන්න"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"සීමා කළ සැකසීම"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ඔබේ ආරක්ෂාව සඳහා, මෙම සැකසීම දැනට නොමැත."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"දුරකථන ඇමතුමක සිටින අතරතුර ක්රියාව ලබා ගත නොහැක."</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"දුරකථන ඇමතුමක් අතරතුර යෙදුම් වලට වෙනත් යෙදුම් ස්ථාපනය කිරීමට ඉඩ නොදෙයි.\n\n වංචාකරුවන් බොහෝ විට දුරකථන ඇමතුම් සංවාද අතරතුර මෙවැනි ක්රියාමාර්ග ඉල්ලා සිටින බැවින්, ඔබව ආරක්ෂා කිරීමට එය අවහිර කර ඇත. ඔබ නොදන්නා කෙනෙකු විසින් මෙම ක්රියාමාර්ගය ගැනීමට ඔබට මඟ පෙන්වනු ලැබුවහොත්, එය වංචාවක් විය හැක."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"යෙදුම <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> වෙත ප්රවේශය ප්රතික්ෂේප කරන ලදි"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"යෙදුම ඔබේ පුද්ගලික සහ මූල්ය තොරතුරු අවදානමට ලක් කළ හැකි සංවේදී අවසරයකට ප්රවේශය ඉල්ලා ඇත.<xliff:g id="ID_1"><br><br></xliff:g>මෙම සීමා කළ අවසරය නොමැතිව යෙදුම නිසි ලෙස ක්රියා නොකරනු ඇත. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ප්රවේශයට ඉඩ දෙන ආකාරය දැන ගන්න</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"යෙදුම පෙරනිමි <xliff:g id="ROLE_NAME">%1$s</xliff:g> වීමට ප්රවේශය ප්රතික්ෂේප කරන ලදි"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"හරි"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"අවසර ඉල්ලීම වළක්වා ඇත"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"මෙම යෙදුම අතිරේක අවසර ඉල්ලා සිටින නමුත්, ප්රවාහ කිරීමේ සැසියක අවසර ලබා දිය නොහැක. පළමුව ඔබේ දුරකථනයෙන් අවසරය ලබා දෙන්න."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"හදිසි ඇමතුම් හෝ පෙළ සඳහා"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ස්ථානය හදිසි සේවා වෙත යවනු ලැබේ"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"මෙම යෙදුම හදිසි ඇමතුම් අංකයකට ඇමතුමක් දෙන හෝ කෙටි පණිවිඩයක් යවන අතරතුර ඔබේ උපාංගයේ ස්ථානයට ප්රවේශ විය. යෙදුමට ස්ථාන අවසරයක් නොමැති විට හෝ උපාංග ස්ථානය ක්රියා විරහිත විට පවා මෙය සිදු විය හැක. "<a href="https://support.google.com/android/answer/9319337">"තව දැන ගන්න"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ඔබේ දුරකථනය පරීක්ෂා කරන්න"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"ඔබේ දුරකථනය වෙත ළඟා විය නොහැකි විය"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"යළි උත්සාහ කරන්න ද?"</string> </resources> diff --git a/PermissionController/res/values-sk-v35/strings.xml b/PermissionController/res/values-sk-v35/strings.xml new file mode 100644 index 000000000..6bd3cb05c --- /dev/null +++ b/PermissionController/res/values-sk-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Zabezpečenie"</string> +</resources> diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml index 73a78befe..4442bbc85 100644 --- a/PermissionController/res/values-sk/strings.xml +++ b/PermissionController/res/values-sk/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"povolenia"</string> <string name="cancel" msgid="8943320028373963831">"Zrušiť"</string> <string name="back" msgid="6249950659061523680">"Späť"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zavrieť"</string> <string name="available" msgid="6007778121920339498">"Dostupné"</string> <string name="blocked" msgid="9195547604866033708">"Blokované"</string> <string name="on" msgid="280241003226755921">"Zapnuté"</string> @@ -205,7 +206,7 @@ <string name="unused_apps_category_title" msgid="2988455616845243901">"Nastavenia nepoužívaných aplikácií"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"Odstrániť povolenia, ak sa aplikácia nepoužíva"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Odstraňovať povol. a uvoľňovať priestor"</string> - <string name="unused_apps_label_v2" msgid="7058776770056517980">"Pozastaviť aktivitu v nepoužívaných apl."</string> + <string name="unused_apps_label_v2" msgid="7058776770056517980">"Pozastaviť aktivitu pri nepoužívaní"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Spravovať aplikáciu, ak sa nepoužíva"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Odstrániť povolenia, vymazať dočasné súbory a zastaviť upozornenia"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Odstrániť povolenia, vymazať dočasné súbory, zastaviť upozornenia a archivovať aplikáciu"</string> @@ -272,7 +273,7 @@ <string name="unused_apps_notification_content" msgid="9195026773244581246">"Povolenia a dočasné súbory boli odstránené a upozornenia boli zastavené. Skontrolujte to klepnutím."</string> <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Skontrolujte aplikácie s odstránenými povoleniami"</string> <string name="unused_apps_safety_center_card_content" msgid="1088557243627427820">"V prípade aplikácií, ktoré ste dlhšie nepoužívali, boli povolenia a dočasné súbory odstránené a upozornenia deaktivované."</string> - <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Skontrolovať aplikácie"</string> + <string name="unused_apps_safety_center_action_title" msgid="8865914432518993194">"Kontrola aplikácií"</string> <string name="post_drive_permission_decision_reminder_title" msgid="1290697371418139976">"Skontrolujte nedávne povolenia"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_1_permission" msgid="670521503734140711">"Počas jazdy ste udelili aplikácii <xliff:g id="APP">%1$s</xliff:g> povolenie <xliff:g id="PERMISSION">%2$s</xliff:g>"</string> <string name="post_drive_permission_decision_reminder_summary_1_app_2_permissions" msgid="671791184670801301">"Počas jazdy ste udelili aplikácii <xliff:g id="APP">%1$s</xliff:g> prístup k povoleniam <xliff:g id="PERMISSION_1">%2$s</xliff:g> & <xliff:g id="PERMISSION_2">%3$s</xliff:g>"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Žiadna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Predvolené systémom)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Žiadne aplikácie"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Ďalšie služby NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Vybraná"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Vybrané – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"špeciálny prístup aplikácií"</string> @@ -669,16 +671,25 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Otvorené včera <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Otvorené <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Jednorazové heslo je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Povoliť obmedzené nastavenia"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Obmedzené nastavenie"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z bezpečnostných dôvodov nie je toto nastavenie momentálne k dispozícii."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Akcia nie je počas telefonovania k dispozícii"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Počas telefonického hovoru nie je povolená inštalácia aplikácií inými aplikáciami.\n\n Podvodníci často žiadajú o tento typ akcie počas telefonických hovorov, preto je daná akcia zablokovaná z dôvodu vašej ochrany. Ak vás o túto akciu žiada niekto, koho nepoznáte, môže ísť o podvod."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikácii bol prístup k povoleniu <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> zamietnutý"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Táto aplikácia vyžaduje prístup k citlivému povoleniu, čo môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ako povoliť prístup</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikácii bol zamietnutý prístup a predvolene nemôže byť <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> - <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Táto aplikácia vyžaduje prístup k citlivým povoleniam, čo môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že bez týchto obmedzených povolení nebude aplikácia správne fungovať. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ako povoliť prístup</a>"</string> + <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikácia požiadala o prístup k citlivým povoleniam, ktoré môžu ohroziť vaše osobné a finančné údaje.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že bez týchto obmedzených povolení nebude aplikácia správne fungovať. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ako povoliť prístup</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikácii bol zamietnutý prístup"</string> - <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Prístup k tomuto povoleniu môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ako povoliť prístup</a>"</string> + <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Prístup k tomuto povoleniu môže ohroziť vaše osobné a finančné údaje.<xliff:g id="ID_1"><br><br></xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Ako povoliť prístup</a>"</string> <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ďalšie informácie"</string> <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Žiadosť o povolenie bola zablokovaná"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Táto aplikácia požaduje ďalšie povolenia, ale povolenia nie je možné udeliť v relácii streamovania. Najprv udeľte povolenie v telefóne."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Na účely tiesňového volania alebo textovej správy"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Poloha bola odoslaná tiesňovej linke"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Táto aplikácia získala počas hovoru s tiesňovou linkou alebo odosielania správy na ňu prístup k polohe vášho zariadenia. Môže sa to stať, aj keď daná aplikácia nemá povolený prístup k polohe alebo keď je poloha zariadenia vypnutá. "<a href="https://support.google.com/android/answer/9319337">"Ďalšie informácie"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Skontrolovať telefón"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"S telefónom sa nepodarilo spojiť"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Chcete to skúsiť znova?"</string> </resources> diff --git a/PermissionController/res/values-sl-v35/strings.xml b/PermissionController/res/values-sl-v35/strings.xml new file mode 100644 index 000000000..e20a0e57b --- /dev/null +++ b/PermissionController/res/values-sl-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Varnost"</string> +</resources> diff --git a/PermissionController/res/values-sl-watch/strings.xml b/PermissionController/res/values-sl-watch/strings.xml index f93ba26b5..3f78007ac 100644 --- a/PermissionController/res/values-sl-watch/strings.xml +++ b/PermissionController/res/values-sl-watch/strings.xml @@ -21,7 +21,7 @@ <string name="preference_show_system_apps" msgid="1055740303992024300">"Prikaz sistemskih aplikacij"</string> <string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ni mogoče sprem."</string> <string name="generic_yes" msgid="2489207724988649846">"Da"</string> - <string name="generic_cancel" msgid="2631708607129269698">"Prekliči"</string> + <string name="generic_cancel" msgid="2631708607129269698">"Preklic"</string> <string name="permission_access_always" msgid="2107115233573823032">"Ves čas"</string> <string name="permission_access_only_foreground" msgid="4412115020089923986">"Med uporabo aplikacije"</string> <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Ves čas"</string> diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml index 198197c83..168646ac1 100644 --- a/PermissionController/res/values-sl/strings.xml +++ b/PermissionController/res/values-sl/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"dovoljenja"</string> <string name="cancel" msgid="8943320028373963831">"Prekliči"</string> <string name="back" msgid="6249950659061523680">"Nazaj"</string> + <string name="dialog_close" msgid="6840699812532384661">"Zapri"</string> <string name="available" msgid="6007778121920339498">"Na voljo"</string> <string name="blocked" msgid="9195547604866033708">"Blokirano"</string> <string name="on" msgid="280241003226755921">"Vklopljeno"</string> @@ -205,7 +206,7 @@ <string name="unused_apps_category_title" msgid="2988455616845243901">"Nastavitve neuporabljenih aplikacij"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"Odstrani dovoljenja, če aplikacija ni v uporabi"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Odstrani dovoljenja in sprosti prostor"</string> - <string name="unused_apps_label_v2" msgid="7058776770056517980">"Zaustavi dejavnost aplikacije ob neuporabi"</string> + <string name="unused_apps_label_v2" msgid="7058776770056517980">"Zaustavi aplikacijo ob neuporabi"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Upravljanje aplikacije ob neuporabi"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Dovoljenja se odstranijo, začasne datoteke se izbrišejo in prikazovanje obvestil se ustavi."</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Odstranitev dovoljenj, izbris začasnih datotek, ustavitev prikazovanja obvestil in arhiviranje aplikacije"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Brez"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(privzeta v sistemu)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ni aplikacij"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Druge storitve NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Izbrano"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Izbrano – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"posebni dostop za aplikacije"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Zadnji dostop včeraj: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Zadnji dostop: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Enkratno geslo je 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Dovoli omejene nastavitve"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Omejena nastavitev"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zaradi vaše varnosti ta nastavitev trenutno ni na voljo."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Dejanje ni na voljo med telefonskim klicem"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Med telefonskim klicem ne morete aplikacijam omogočiti nameščanja drugih aplikacij.\n\n Prevaranti med pogovori v telefonskih klicih pogosto zahtevajo to vrsto dejanja, zato je to dejanje blokirano zaradi vaše zaščite. Če vas k temu dejanju napeljuje nekdo, ki ga ne poznate, gre morda za poskus prevare."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je bil zavrnjen dostop do dovoljenja »<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>«"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zahtevala dostop do občutljivega dovoljenja, ki lahko ogrozi vaše osebne in finančne podatke.<xliff:g id="ID_1"><br><br></xliff:g>Aplikacija morda ne bo pravilno delovala brez tega omejenega dovoljenja. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Preberite, kako omogočite dostop</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je bil zavrnjen dostop do privzete vloge »<xliff:g id="ROLE_NAME">%1$s</xliff:g>«"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"V redu"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahteva za dovoljenje je bila prezrta"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ta aplikacija zahteva dodatna dovoljenja, vendar teh ni mogoče odobriti med sejo pretočnega predvajanja. Dovoljenje najprej odobrite v telefonu."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za klic ali sporočilo v sili"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija je poslana reševalnim službam"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ta aplikacija je dostopala do lokacije naprave med klicanjem ali pošiljanjem sporočila na številko za klic v sili. To se lahko zgodi tudi, če aplikacija nima dovoljenja za lokacijo ali če je lokacija naprave izklopljena. "<a href="https://support.google.com/android/answer/9319337">"Več o tem"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Preverite telefon"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Telefona ni bilo mogoče doseči"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Ali želite poskusiti znova?"</string> </resources> diff --git a/PermissionController/res/values-sq-v35/strings.xml b/PermissionController/res/values-sq-v35/strings.xml new file mode 100644 index 000000000..ef8b1c486 --- /dev/null +++ b/PermissionController/res/values-sq-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Siguria"</string> +</resources> diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml index 15209188a..793b1beb1 100644 --- a/PermissionController/res/values-sq/strings.xml +++ b/PermissionController/res/values-sq/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"lejet"</string> <string name="cancel" msgid="8943320028373963831">"Anulo"</string> <string name="back" msgid="6249950659061523680">"Pas"</string> + <string name="dialog_close" msgid="6840699812532384661">"Mbyll"</string> <string name="available" msgid="6007778121920339498">"Ofrohet"</string> <string name="blocked" msgid="9195547604866033708">"Bllokuar"</string> <string name="on" msgid="280241003226755921">"Aktiv"</string> @@ -440,10 +441,11 @@ <string name="default_app_none" msgid="9084592086808194457">"Asnjë"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Parazgjedhja e sistemit)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Nuk ka aplikacione"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Shërbime të tjera të NFC-së"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Zgjedhur"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Zgjedhur - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"qasje e veçantë e aplikacionit"</string> - <string name="special_app_access" msgid="5019319067120213797">"Qasje e veçantë aplikacioni"</string> + <string name="special_app_access" msgid="5019319067120213797">"Qasja e veçantë e apl."</string> <string name="no_special_app_access" msgid="6950277571805106247">"Jo qasje e veçantë aplikacioni"</string> <string name="special_app_access_no_apps" msgid="4102911722787886970">"Nuk ka aplikacione"</string> <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Profili i punës nuk mbështetet"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Qasja e fundit dje në <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Qasja e fundit më <xliff:g id="TIME_DATE_0">%1$s</xliff:g> në <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Fjalëkalimi yt njëpërdorimësh është 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Lejo cilësimet e kufizuara"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cilësim i kufizuar"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Për sigurinë tënde, ky cilësim nuk ofrohet për momentin."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Veprimi nuk ofrohet kur je në një telefonatë"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Nuk lejohet që aplikacionet të lejohen të instalojnë aplikacione të tjera gjatë një telefonate.\n\n Mashtruesit e kërkojnë shpesh këtë lloj veprimi gjatë bisedave të telefonatave, prandaj kjo është bllokuar për të të mbrojtur ty. Nëse po udhëzohesh ta kryesh këtë veprim nga dikush që nuk e njeh, mund të jetë një mashtrim."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacionit iu refuzua qasja te: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacioni kërkoi qasje në një leje delikate që mund t\'i vendosë në rrezik informacionet e tua personale dhe financiare.<xliff:g id="ID_1"><br><br></xliff:g>Ka mundësi që aplikacioni të mos funksionojë si duhet pa këtë leje të kufizuar. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Mëso se si të lejosh qasjen</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacionit iu refuzua qasja për të qenë parazgjedhja për: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Në rregull"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Kërkesa për leje është ndaluar"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ky aplikacion po kërkon leje shtesë, por lejet nuk mund të jepen në një seancë transmetimi. Fillimisht jep lejen në telefonin tënd."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Për telefonatën apo mesazhin me tekst të urgjencës"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Vendndodhja u dërgua te shërbimet e urgjencës"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ky aplikacion u qas te vendndodhja e pajisjes sate gjatë një telefonate apo mesazhi me tekst drejt një numri urgjence. Kjo mund të ndodhë edhe kur aplikacioni nuk ka lejen për vendndodhjen ose kur vendndodhja e pajisjes është joaktive. "<a href="https://support.google.com/android/answer/9319337">"Mëso më shumë"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Kontrollo telefonin"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Telefoni nuk mund të arrihej"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Të riprovohet?"</string> </resources> diff --git a/PermissionController/res/values-sr-v35/strings.xml b/PermissionController/res/values-sr-v35/strings.xml new file mode 100644 index 000000000..3690e7e0e --- /dev/null +++ b/PermissionController/res/values-sr-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Безбедност"</string> +</resources> diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml index 135c2f08c..0c6fd04f6 100644 --- a/PermissionController/res/values-sr/strings.xml +++ b/PermissionController/res/values-sr/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"дозволе"</string> <string name="cancel" msgid="8943320028373963831">"Откажи"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Затвори"</string> <string name="available" msgid="6007778121920339498">"Доступно"</string> <string name="blocked" msgid="9195547604866033708">"Блокирано"</string> <string name="on" msgid="280241003226755921">"Укључено"</string> @@ -368,7 +369,7 @@ <string name="role_dialer_search_keywords" msgid="3324448983559188087">"бројчаник"</string> <string name="role_sms_label" msgid="8456999857547686640">"Подразумевана апл. за SMS"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"Апликација за SMS"</string> - <string name="role_sms_description" msgid="3424020199148153513">"Апликације које вам омогућавају да користите број телефона да бисте слали и примали кратке SMS-ове, слике, видео снимке и још тога"</string> + <string name="role_sms_description" msgid="3424020199148153513">"Апликације које вам омогућавају да користите број телефона да бисте слали и примали кратке текстуалне поруке, слике, видео снимке и друго"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацју за SMS?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Ова апликација ће добити приступ камери, контактима, фајловима и медијима, микрофону, телефону и SMS-овима"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"SMS, слање SMS-ова, поруке, слање порука"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Ништа"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Подразумевана системска)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Нема апликација"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Друге NFC услуге"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Изабрано"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Изабрано – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"посебан приступ апликација"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> жели да отпреми информације за отклањање грешака."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Желите да делите податке о отклањању грешака?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Систем је открио проблем."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> тражи да отпреми извештај о грешкама са овог уређаја који је направљен <xliff:g id="DATE">%2$s</xliff:g> у <xliff:g id="TIME">%3$s</xliff:g>. Извештаји о грешкама обухватају личне податке о уређају или податке које су евидентирале апликације, на пример, корисничка имена, податке о локацији, идентификаторе уређаја и информације о мрежи. Делите извештаје о грешкама само са људима и апликацијама којима можете да поверите те информације. Желите ли да дозволите да <xliff:g id="APP_NAME_1">%4$s</xliff:g> отпреми извештај о грешци?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> тражи да отпреми извештај о грешкама са овог уређаја који је направљен <xliff:g id="DATE">%2$s</xliff:g> у <xliff:g id="TIME">%3$s</xliff:g>. Извештаји о грешкама обухватају личне податке о уређају или податке које су евидентирале апликације, на пример, корисничка имена, податке о локацији, идентификаторе уређаја и информације о мрежи. Делите извештаје о грешкама само са људима и апликацијама које сматрате поузданим за те информације. Желите ли да дозволите да <xliff:g id="APP_NAME_1">%4$s</xliff:g> отпреми извештај о грешци?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Дошло је до грешке при обради извештаја о грешци за апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>. Зато је одбијено дељење детаљних података о отклањању грешака. Извињавамо се због прекида."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Дозволи"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Одбиј"</string> @@ -469,10 +471,10 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа сликама и медијима на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа контактима?"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа контактима на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"Желите да дозволите да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији уређаја <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Апликација ће имати приступ локацији само док користите апликацију"</string> - <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string> + <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Желите да дозволите да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string> <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији уређаја <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ова апликација можда жели да приступа локацији све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string> <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Желите ли да промените приступ локацији за апликацију <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> @@ -514,13 +516,13 @@ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа подацима о физичким активностима на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_camera" msgid="5123097035410002594">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима слике и видео?"</string> <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима слике и видео на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Апликација ће моћи да снима слике и видео снимке само док користите апликацију"</string> - <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима слике и видео снимке?"</string> + <string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Апликација ће моћи да снима слике и видео само док користите апликацију"</string> + <string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима слике и видео?"</string> <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снима слике и видео на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ова апликација можда жели да снима слике и видео снимке све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string> + <string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ова апликација можда жели да снима слике и видео све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string> <string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Желите да промените приступ камери за апликацију <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Мењате приступ камери за апликацију <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> - <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ова апликација жели да снима слике и видео снимке све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string> + <string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ова апликација жели да снима слике и видео све време, чак и када не користите апликацију. "<annotation id="link">"Дозволите у подешавањима."</annotation></string> <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа евиденцијама позива на телефону?"</string> <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Дозвољавате да апликација <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа евиденцији телефонских позива на: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_phone" msgid="1829234136997316752">"Желите да дозволите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> упућује позиве и управља њима?"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Приступано јуче: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Приступано: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Једнократна лозинка је 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволи ограничена подешавања"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничено подешавање"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ово подешавање је тренутно недоступно ради ваше безбедности."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Радња није доступна током телефонског позива"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Није дозвољено да апликације инсталирају друге апликације током телефонског позива.\n\n Преваранти често траже овај тип радње током телефонског позива, па смо је блокирали да бисмо вас заштитили. Ако вас неко кога не познајете упућује на ову радњу, то би могла да буде превара."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Апликацији није дозвољен приступ дозволи: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликација је затражила приступ осетљивој дозволи, што може да угрози безбедност личних и финансијских података.<xliff:g id="ID_1"><br><br></xliff:g>Апликација можда неће радити исправно без ове ограничене дозволе. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Сазнајте како да дозволите приступ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Апликацији није дозвољен приступ да постане подразумевана: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Важи"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Захтев за дозволу је блокиран"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ова апликација захтева додатне дозволе, али дозволе не могу да се дају у сесији стримовања. Прво дајте дозволу на телефону."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"За хитни позив или поруку"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Локација је послата хитним службама"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ова апликација је приступила локацији уређаја током позивања броја за хитне случајеве или слања поруке том броју. То може да се догоди чак и када апликација нема дозволу за локацију или је локација уређаја искључена. "<a href="https://support.google.com/android/answer/9319337">"Сазнајте више"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Проверите телефон"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Повезивање са телефоном није успело"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Желите ли да пробате поново?"</string> </resources> diff --git a/PermissionController/res/values-sv-v35/strings.xml b/PermissionController/res/values-sv-v35/strings.xml new file mode 100644 index 000000000..c3d1b1b1c --- /dev/null +++ b/PermissionController/res/values-sv-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Säkerhet"</string> +</resources> diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml index a5e58b674..3d0f56de0 100644 --- a/PermissionController/res/values-sv/strings.xml +++ b/PermissionController/res/values-sv/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"behörigheter"</string> <string name="cancel" msgid="8943320028373963831">"Avbryt"</string> <string name="back" msgid="6249950659061523680">"Tillbaka"</string> + <string name="dialog_close" msgid="6840699812532384661">"Stäng"</string> <string name="available" msgid="6007778121920339498">"Tillgänglig"</string> <string name="blocked" msgid="9195547604866033708">"Blockerad"</string> <string name="on" msgid="280241003226755921">"På"</string> @@ -355,7 +356,7 @@ <string name="role_assistant_label" msgid="4727586018198208128">"Digital assistentapp, standard"</string> <string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistentapp"</string> <string name="role_assistant_description" msgid="6622458130459922952">"Med assistentappar kan du få hjälp som baseras på den information som visas på den aktuella skärmen. Vissa appar har stöd för både översikts- och röstinmatningstjänster för att hjälpa dig."</string> - <string name="role_browser_label" msgid="2877796144554070207">"Standard webbläsarapp"</string> + <string name="role_browser_label" msgid="2877796144554070207">"Standardwebbläsarapp"</string> <string name="role_browser_short_label" msgid="6745009127123292296">"Webbläsarapp"</string> <string name="role_browser_description" msgid="3465253637499842671">"Appar som visar länkar du trycker på och du använder för att ansluta till internet"</string> <string name="role_browser_request_title" msgid="2895200507835937192">"Vill du ställa in <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardwebbläsarapp?"</string> @@ -436,10 +437,11 @@ <string name="default_apps_more" msgid="4078194675848858093">"Fler standardappar"</string> <string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Öppna länkar"</string> <string name="default_apps_for_work" msgid="4970308943596201811">"Standardinställning för jobbet"</string> - <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Standard för privat område"</string> + <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Standard för privat utrymme"</string> <string name="default_app_none" msgid="9084592086808194457">"Ingen"</string> - <string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string> + <string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Inga appar"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andra NFC-tjänster"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Vald"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Vald – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"särskild appåtkomst"</string> @@ -448,13 +450,13 @@ <string name="special_app_access_no_apps" msgid="4102911722787886970">"Inga appar"</string> <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Jobbprofiler stöds inte"</string> <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Obs! Om du startar om mobilen och har ställt in ett skärmlås kan appen inte startas förrän du låser upp mobilen."</string> - <string name="assistant_confirmation_message" msgid="7476540402884416212">"Assistenten kan läsa information om appar som används i systemet, inklusive information som visas på skärmen eller är åtkomlig i apparna."</string> + <string name="assistant_confirmation_message" msgid="7476540402884416212">"Assistent kan läsa information om appar som används i systemet, inklusive information som visas på skärmen eller är åtkomlig i apparna."</string> <string name="incident_report_channel_name" msgid="3144954065936288440">"Dela felsökningsinformation"</string> <string name="incident_report_notification_title" msgid="4635984625656519773">"Vill du dela detaljerad felsökningsinformation?"</string> <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> vill ladda upp felsökningsinformation."</string> - <string name="incident_report_dialog_title" msgid="669104389325204095">"Vill du dela felsökningsinformation?"</string> + <string name="incident_report_dialog_title" msgid="669104389325204095">"Vill du dela felsökningsinformation?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Ett problem har upptäckts i systemet."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> begär tillstånd att ladda upp en felrapport som sparades den <xliff:g id="DATE">%2$s</xliff:g> kl. <xliff:g id="TIME">%3$s</xliff:g> från den här enheten. Felrapporter innehåller personlig information om enheten eller information som loggats av appar, t.ex. användarnamn, platsdata, enhetsidentifierare och nätverksinformation. Dela bara felrapporter med personer och appar du litar på. Tillåter du att <xliff:g id="APP_NAME_1">%4$s</xliff:g> laddar upp en felrapport?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> begär tillstånd att ladda upp en felrapport som sparades <xliff:g id="DATE">%2$s</xliff:g> kl. <xliff:g id="TIME">%3$s</xliff:g> från den här enheten. Felrapporter innehåller personlig information om enheten eller information som loggats av appar, t.ex. användarnamn, platsdata, enhetsidentifierare och nätverksinformation. Dela bara felrapporter med personer och appar du litar på. Tillåter du att <xliff:g id="APP_NAME_1">%4$s</xliff:g> laddar upp en felrapport?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Ett fel uppstod när felrapporten för <xliff:g id="APP_NAME">%1$s</xliff:g> skulle behandlas. Därför har delning av utförlig felsökningsdata nekats. Ursäkta avbrottet."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Tillåt"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Neka"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Öppnades i går <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Öppnades <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ditt engångslösenord är 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Tillåt blockerade inställningar"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begränsad inställning"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av säkerhetsskäl är den här inställningen inte tillgänglig för närvarande."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Åtgärden är inte tillgänglig under pågående samtal"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Det går inte att tillåta att appar installerar andra appar under ett telefonsamtal.\n\n Bedragare vill ofta att man gör sådana här saker under telefonsamtalet, så vi möjligheten blockeras för att skydda dig. Om du blir tillsagd att göra detta av någon du inte känner kan det vara ett bedrägeri."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen nekades åtkomst till <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen begärde åtkomstbehörighet till känsliga uppgifter, vilket kan utsätta din personliga och finansiella information för risk.<xliff:g id="ID_1"><br><br></xliff:g>Appen kanske inte fungerar som den ska utan denna begränsade behörighet. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gtLäs mer om hur du tillåter åtkomst</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen nekades åtkomst till standardrollen för <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Begäran om behörighet har dolts"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Appen begär ytterligare behörigheter, men det går inte att bevilja behörigheter under streamingsessionen. Bevilja behörigheten på telefonen först."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"För ett nödsamtal eller nöd-sms"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Plats skickad till räddningstjänsten"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Den här appen fick åtkomst till enhetens plats när du ringde eller sms:ade till ett nödnummer. Detta kan hända även när appen inte har platsbehörighet eller om enhetens plats är inaktiverad. "<a href="https://support.google.com/android/answer/9319337">"Läs mer"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Kontrollera telefonen"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Det gick inte att nå telefonen"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Vill du försöka igen?"</string> </resources> diff --git a/PermissionController/res/values-sw-v35/strings.xml b/PermissionController/res/values-sw-v35/strings.xml new file mode 100644 index 000000000..6234b97ec --- /dev/null +++ b/PermissionController/res/values-sw-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Usalama"</string> +</resources> diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml index 08766ae6a..290d56807 100644 --- a/PermissionController/res/values-sw/strings.xml +++ b/PermissionController/res/values-sw/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ruhusa"</string> <string name="cancel" msgid="8943320028373963831">"Ghairi"</string> <string name="back" msgid="6249950659061523680">"Rudi nyuma"</string> + <string name="dialog_close" msgid="6840699812532384661">"Funga"</string> <string name="available" msgid="6007778121920339498">"Inaruhusiwa kufikia"</string> <string name="blocked" msgid="9195547604866033708">"Imezuiwa kufikia"</string> <string name="on" msgid="280241003226755921">"Imewashwa"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Hakuna"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Programu chaguomsingi ya mfumo)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Hakuna programu"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Huduma nyinginezo za NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Umechagua"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Umechagua - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ufikiaji maalum wa programu"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> inataka kupakia maelezo ya utatuzi."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Ungependa kushiriki data ya utatuzi?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Mfumo umetambua hitilafu."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> inaomba kupakia ripoti ya hitilafu kutoka kifaa hiki, iliyochakatwa tarehe <xliff:g id="DATE">%2$s</xliff:g> saa <xliff:g id="TIME">%3$s</xliff:g>. Ripoti za hitilafu hujumuisha taarifa binafsi kuhusu kifaa chako au iliyohifadhiwa na programu, kwa mfano, majina ya watumiaji, data ya mahali, vitambulishi vya vifaa na maelezo ya mtandao. Shiriki tu ripoti za hitilafu na watu au programu unazoamini kupokea maelezo haya. Ungependa kuruhusu <xliff:g id="APP_NAME_1">%4$s</xliff:g> ipakie ripoti ya hitilafu?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> inaomba kupakia ripoti ya hitilafu kutoka kifaa hiki, iliyochakatwa tarehe <xliff:g id="DATE">%2$s</xliff:g> saa <xliff:g id="TIME">%3$s</xliff:g>. Ripoti za hitilafu hujumuisha taarifa binafsi kuhusu kifaa chako au iliyohifadhiwa na programu, kwa mfano, majina ya watumiaji, data ya mahali, vitambulisho vya vifaa na maelezo ya mtandao. Tuma tu ripoti za hitilafu kwa watu au programu unazoamini. Ungependa kuruhusu <xliff:g id="APP_NAME_1">%4$s</xliff:g> ipakie ripoti ya hitilafu?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Hitilafu imetokea wakati wa kuchakata ripoti ya hitilafu ya <xliff:g id="APP_NAME">%1$s</xliff:g>. Kwa hivyo, kushiriki data ya maelezo ya kina ya utatuzi kumekataliwa. Samahani kwa usumbufu."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Ruhusu"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Kataa"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kilifunguliwa jana <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kilifunguliwa <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Nenosiri lako la wakati mmoja ni 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Ruhusu mipangilio yenye mipaka"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Mipangilio imezuiwa"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ili kulinda usalama wako, mipangilio hii haipatikani kwa sasa."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Kitendo hiki hakipatikani unapopiga simu"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Programu haziruhusiwi kuweka programu nyingine kwenye kifaa wakati wa mazungumzo ya simu.\n\n Mara nyingi, walaghai hukuomba utekeleze vitendo vya aina hii wakati wa mazungumzo ya simu, kwa hivyo kimezuiwa ili kukulinda. Iwapo unaelekezwa utekeleze kitendo hiki na mtu usiyemjua, huenda ikawa ni ulaghai."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programu haijapewa idhini ya kufikia <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programu imeomba kufikia ruhusa nyeti, hali ambayo inaweza kuhatarisha maelezo yako ya kifedha na ya binafsi.<xliff:g id="ID_1"><br><br></xliff:g>Kuna uwezekano kuwa programu haitafanya kazi vizuri bila ruhusa hii inayodhibitiwa. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Pata maelezo kuhusu jinsi ya kuruhusu ufikiaji</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programu haijapewa idhini ya kuwa <xliff:g id="ROLE_NAME">%1$s</xliff:g> chaguomsingi"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Sawa"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Ombi la ruhusa limezuiwa"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Programu hii inaomba ruhusa za ziada. Hata hivyo, huwezi kutoa ruhusa ukitiririsha. Ruhusu kwenye simu yako kwanza."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Kwa kutuma ujumbe au kupiga simu ya dharura"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Data ya mahali imetumwa kwa huduma za dharura"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Programu hii ilifikia data ya mahali kilipo kifaa chako ulipokuwa ukipiga simu au kutuma ujumbe kwa namba ya dharura. Hali hii inaweza kutokea hata kama programu haina ruhusa ya mahali au umezima kipengele cha kufikia data ya mahali kifaa kilipo. "<a href="https://support.google.com/android/answer/9319337">"Pata maelezo zaidi"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Angalia simu yako"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Imeshindwa kufikia simu yako"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Ungependa kujaribu tena?"</string> </resources> diff --git a/PermissionController/res/values-ta-v35/strings.xml b/PermissionController/res/values-ta-v35/strings.xml new file mode 100644 index 000000000..6c6adaf80 --- /dev/null +++ b/PermissionController/res/values-ta-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"பாதுகாப்பு"</string> +</resources> diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml index 601ae1f30..8e1b71b97 100644 --- a/PermissionController/res/values-ta/strings.xml +++ b/PermissionController/res/values-ta/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"அனுமதிகள்"</string> <string name="cancel" msgid="8943320028373963831">"ரத்துசெய்"</string> <string name="back" msgid="6249950659061523680">"பின்செல்"</string> + <string name="dialog_close" msgid="6840699812532384661">"மூடு"</string> <string name="available" msgid="6007778121920339498">"உள்ளது"</string> <string name="blocked" msgid="9195547604866033708">"தடுக்கப்பட்டது"</string> <string name="on" msgid="280241003226755921">"இயக்கப்பட்டுள்ளது"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ஏதுமில்லை"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(சிஸ்டத்தின் இயல்புநிலை)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ஆப்ஸ் இல்லை"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"பிற NFC சேவைகள்"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"தேர்ந்தெடுக்கப்பட்டது"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g> தேர்ந்தெடுக்கப்பட்டுள்ளது"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ஆப்ஸிற்கான சிறப்பு அணுகல்"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"நேற்று அணுகிய நேரம்: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"அணுகிய நேரம்: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"ஒருமுறை பயன்படுத்தப்படும் கடவுச்சொல்: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"கட்டுப்படுத்தப்பட்ட அமைப்பை அனுமதி"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"கட்டுப்படுத்தப்பட்ட அமைப்பு"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"உங்கள் பாதுகாப்பிற்காக, இந்த அமைப்பு தற்போது இல்லை."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"மொபைல் அழைப்பின்போது இந்தச் செயலைச் செய்ய முடியாது"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"மொபைல் அழைப்பின்போது ஓர் ஆப்ஸ் பிற ஆப்ஸை நிறுவ அனுமதிப்பது அனுமதிக்கப்படாது.\n\n மொபைல் அழைப்பு உரையாடல்களின்போது, மோசடி செய்பவர்கள் அடிக்கடி இதுபோன்ற செயலைச் செய்யுமாறு கேட்கின்றனர், எனவே இவற்றிலிருந்து உங்களைப் பாதுகாக்கவே அனுமதி தடுக்கப்பட்டுள்ளது. உங்களுக்குத் தெரியாத ஒருவரால் இந்தச் செயலைச் செய்வதற்கு நீங்கள் வழிகாட்டப்பட்டால் அது மோசடியாக இருக்கலாம்."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"பாதுகாக்க வேண்டிய தகவல்களுக்கான அனுமதியை வழங்க ஆப்ஸ் கேட்டுள்ளது, இது உங்கள் தனிப்பட்ட மற்றும் நிதித் தகவல்களை ஆபத்துக்குள்ளாக்கலாம்.<xliff:g id="ID_1"><br><br></xliff:g>இந்தக் கட்டுப்படுத்தப்பட்ட அனுமதி இல்லாமல் ஆப்ஸ் சரியாக வேலை செய்யாமல் போகக்கூடும். <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>எப்படி அணுகல் வழங்குவதென அறிக</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"இயல்புநிலை <xliff:g id="ROLE_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"சரி"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"அணுகல் கோரிக்கை முடக்கப்பட்டது"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"இந்த ஆப்ஸ் கூடுதல் அனுமதிகளைக் கேட்கிறது. ஆனால் ஸ்ட்ரீமிங் அமர்வில் அனுமதிகள் வழங்கப்படாது. முதலில் உங்கள் மொபைலில் அனுமதி வழங்கவும்."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"அவசர அழைப்பு/மெசேஜுக்கு"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"அவசரகாலச் சேவைகளுக்கு இருப்பிடம் அனுப்பப்பட்டது"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"அவசர உதவி எண்ணை அழைத்தபோதோ அந்த எண்ணிற்கு மெசேஜ் அனுப்பியபோதோ உங்கள் சாதனத்தின் இருப்பிடத்தை இந்த ஆப்ஸ் அணுகியது. இருப்பிட அனுமதி ஆப்ஸுக்கு இல்லாதபோதும் சாதனத்தின் இருப்பிடம் முடக்கப்பட்டிருக்கும்போதும் கூட இப்படி நிகழலாம். "<a href="https://support.google.com/android/answer/9319337">"மேலும் அறிக"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"மொபைலைப் பாருங்கள்"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"உங்கள் மொபைலுடன் இணைக்க முடியவில்லை"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"மீண்டும் முயலவா?"</string> </resources> diff --git a/PermissionController/res/values-te-v35/strings.xml b/PermissionController/res/values-te-v35/strings.xml new file mode 100644 index 000000000..7285aa987 --- /dev/null +++ b/PermissionController/res/values-te-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"సెక్యూరిటీ"</string> +</resources> diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml index 141a5c2ff..e920ad698 100644 --- a/PermissionController/res/values-te/strings.xml +++ b/PermissionController/res/values-te/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"అనుమతులు"</string> <string name="cancel" msgid="8943320028373963831">"రద్దు చేయండి"</string> <string name="back" msgid="6249950659061523680">"వెనుకకు"</string> + <string name="dialog_close" msgid="6840699812532384661">"మూసివేయండి"</string> <string name="available" msgid="6007778121920339498">"అందుబాటులో ఉంది"</string> <string name="blocked" msgid="9195547604866033708">"బ్లాక్ చేయబడినది"</string> <string name="on" msgid="280241003226755921">"ఆన్లో ఉంది"</string> @@ -237,7 +238,7 @@ <string name="permission_description_summary_camera" msgid="108004375101882069">"ఈ అనుమతి ఉన్న యాప్లు ఫోటోలు తీయగలవు, వీడియోను రికార్డ్ చేయగలవు"</string> <string name="permission_description_summary_contacts" msgid="2337798886460408996">"ఈ అనుమతి ఉన్న యాప్లు మీ కాంటాక్ట్లను యాక్సెస్ చేయగలవు"</string> <string name="permission_description_summary_location" msgid="2817531799933480694">"ఈ అనుమతి ఉన్న యాప్లు ఈ పరికర లొకేషన్ను యాక్సెస్ చేయగలవు"</string> - <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"ఈ అనుమతి ఉన్న యాప్లు సమీప పరికరాలను గుర్తించవచ్చు, వాటి సంబంధిత స్థానాన్ని తెలుసుకోవచ్చు, అలాగే వాటికి కనెక్ట్ చేయవచ్చు"</string> + <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"ఈ అనుమతి ఉన్న యాప్లు సమీప పరికరాలను గుర్తించవచ్చు, వాటి సంబంధిత పొజిషన్ను తెలుసుకోవచ్చు, అలాగే వాటికి కనెక్ట్ చేయవచ్చు"</string> <string name="permission_description_summary_microphone" msgid="630834800308329907">"ఈ అనుమతి ఉన్న యాప్లు ఆడియోను రికార్డ్ చేయగలవు"</string> <string name="permission_description_summary_phone" msgid="4515277217435233619">"ఈ అనుమతులు ఉన్న యాప్లు ఫోన్ కాల్స్ చేయగలవు, మేనేజ్ చేయగలవు"</string> <string name="permission_description_summary_sensors" msgid="1836045815643119949">"ఈ అనుమతి ఉన్న యాప్లు మీ ఆరోగ్య స్థితిని తెలియజేసే గణాంకాల సెన్సార్ డేటాను యాక్సెస్ చేయగలవు"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ఏదీ కాదు"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(సిస్టమ్ ఆటోమేటిక్)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ఏ యాప్ లేదు"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"ఇతర NFC సర్వీస్లు"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"ఎంచుకోబడింది"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"<xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g> - ఎంచుకోబడింది"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ప్రత్యేక యాప్ యాక్సెస్"</string> @@ -478,16 +480,16 @@ <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> కోసం లొకేషన్ యాక్సెస్ను మార్చాలా?"</string> <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>లో <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>కు సంబంధించిన లొకేషన్ యాక్సెస్ను మార్చాలా?"</string> <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"మీరు యాప్ ఉపయోగించనప్పుడు కూడా ఈ యాప్ మీ లొకేషన్ను ఎప్పటికప్పుడు యాక్సెస్ చేయాలని అనుకుంటోంది."<annotation id="link">"సెట్టింగ్లలో అనుమతించండి."</annotation></string> - <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>\"ను అనుమతించాలా?"</string> + <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత పొజిషన్ను కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>\"ను అనుమతించాలా?"</string> <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>లో సమీప పరికరాలు కనుగొని, కనెక్ట్ అయి, వాటి దూరం అంచనా వేసేలా <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> - <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్లలో అనుమతించండి."</annotation></string> + <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత పొజిషన్ను కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్లలో అనుమతించండి."</annotation></string> <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME"><b>%1$s</b></xliff:g>కు సంబంధించిన లొకేషన్ యాక్సెస్ను సుమారు నుండి ఖచ్చితమైనదిగా మార్చాలా?"</string> <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>లో <b><xliff:g id="APP_NAME"><b>%1$s</b></xliff:g></b> లొకేషన్ యాక్సెస్ను రమారమి నుండి ఖచ్చితమైన లొకేషన్కు మార్చాలా?"</string> <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ఈ పరికరానికి సంబంధించి సుమారుగా ఉన్న లొకేషన్ను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string> <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b> రమారమి లొకేషన్ను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> యాప్ను అనుమతించాలనుకుంటున్నారా?"</string> <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"ఖచ్చితమైన"</string> <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"సుమారుగా"</string> - <string name="permgrouprequest_calendar" msgid="1493150855673603806">"మీ క్యాలెండర్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string> + <string name="permgrouprequest_calendar" msgid="1493150855673603806">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>లో మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> యాప్ను అనుమతించాలనుకుంటున్నారా?"</string> <string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS మెసేజ్లు పంపడం, చూడటం చేయగలిగేలా <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>లో SMS మెసేజ్లను పంపడానికి, వాటిని చూడటానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"నిన్న <xliff:g id="TIME_DATE">%1$s</xliff:g>కు యాక్సెస్ చేశారు"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g>న<xliff:g id="TIME_DATE_1">%2$s</xliff:g>కు యాక్సెస్ చేశారు"</string> <string name="test_otp_msg" msgid="7559110574222727550">"మీ ఒకసారి ఉపయోగించగల పాస్వర్డ్ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"పరిమితం చేసిన సెట్టింగ్లను అనుమతించండి"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"పరిమితం చేయబడిన సెట్టింగ్"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"మీ సెక్యూరిటీ కోసం, ఈ సెట్టింగ్ ప్రస్తుతం అందుబాటులో లేదు."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"ఫోన్ కాల్లో ఉండగా ఈ చర్య అందుబాటులో ఉండదు"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ఫోన్ కాల్ మాట్లాడే సమయంలో ఇతర యాప్లను ఇన్స్టాల్ చేయడానికి యాప్లను అనుమతించడానికి వీలు లేదు.\n\n ఫోన్ కాల్ సంభాషణల సమయంలో స్కామర్లు తరచుగా ఈ రకమైన చర్యను రిక్వెస్ట్ చేస్తారు, కాబట్టి మిమ్మల్ని రక్షించడానికి ఈ ఫీచర్ బ్లాక్ చేయబడుతుంది. మీకు తెలియని వారు ఈ చర్య తీసుకోవాల్సిందిగా మిమ్మల్ని గైడ్ చేస్తే, అది స్కామ్ కావచ్చు."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"యాప్ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>కు యాక్సెస్ తిరస్కరించబడింది"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"మీ వ్యక్తిగత, ఆర్థిక సమాచారాన్ని ప్రమాదంలో పడేసే గోప్యమైన సమాచార యాక్సెస్ అనుమతికి యాప్ యాక్సెస్ను రిక్వెస్ట్ చేయడం జరిగింది.<xliff:g id="ID_1"><br><br></xliff:g>ఈ అదనపు అవసరాలు గల అనుమతి లేకుండా యాప్ సరిగ్గా పని చేయకపోయే అవకాశం ఉంది. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>యాక్సెస్ను అనుమతించడం ఎలాగో తెలుసుకోండి</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ఆటోమేటిక్ <xliff:g id="ROLE_NAME">%1$s</xliff:g>గా ఉండటానికి యాప్నకు యాక్సెస్ నిరాకరించబడింది"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"సరే"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"అనుమతి రిక్వెస్ట్ బ్లాక్ చేయబడింది"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ఈ యాప్ అదనపు అనుమతి కోసం రిక్వెస్ట్ చేస్తోంది, కానీ స్ట్రీమింగ్ సెషన్లో అనుమతులను మంజూరు చేయడం సాధ్యం కాదు. ముందుగా మీ ఫోన్లో అనుమతిని మంజూరు చేయండి."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ఎమర్జెన్సీ కాల్ లేదా టెక్స్ట్ మెసేజ్ పంపే విషయంలో"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ఎమర్జెన్సీ సర్వీసులకు లొకేషన్ పంపడం జరిగింది"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ఎమర్జెన్సీ నంబర్కు కాల్ చేసినప్పుడు, లేదా టెక్స్ట్ మెసేజ్ పంపినప్పుడు ఈ యాప్ మీ పరికర లొకేషన్ను యాక్సెస్ చేసింది. యాప్నకు లొకేషన్ అనుమతి లేకున్నా లేదా పరికర లొకేషన్ ఆఫ్లో ఉన్నప్పటికీ కూడా ఇలా జరిగవచ్చు. "<a href="https://support.google.com/android/answer/9319337">"మరింత తెలుసుకోండి"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"మీ ఫోన్ను చెక్ చేయండి"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"మీ ఫోన్ను రీచ్ అవడం సాధ్యపడలేదు"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"మళ్లీ ట్రై చేయాలా?"</string> </resources> diff --git a/PermissionController/res/values-television/styles.xml b/PermissionController/res/values-television/styles.xml index 05ed588e9..3a54ce8b2 100644 --- a/PermissionController/res/values-television/styles.xml +++ b/PermissionController/res/values-television/styles.xml @@ -23,7 +23,7 @@ </style> <style name="LeanbackPreference.Permissions"> - <item name="layout">@layout/preference_permissions</item> + <item name="layout">@layout/preference_permissions_television</item> </style> <style name="LeanbackPreference.Category.Permissions"> @@ -31,7 +31,7 @@ </style> <style name="LeanbackPreference.SwitchPreference.Permissions"> - <item name="layout">@layout/preference_permissions_switch</item> + <item name="layout">@layout/preference_permissions_switch_television</item> </style> <!-- Request role dialog start --> diff --git a/PermissionController/res/values-th-v35/strings.xml b/PermissionController/res/values-th-v35/strings.xml new file mode 100644 index 000000000..cf819a8ce --- /dev/null +++ b/PermissionController/res/values-th-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"ความปลอดภัย"</string> +</resources> diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml index cb0a56061..45b34108b 100644 --- a/PermissionController/res/values-th/strings.xml +++ b/PermissionController/res/values-th/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"สิทธิ์"</string> <string name="cancel" msgid="8943320028373963831">"ยกเลิก"</string> <string name="back" msgid="6249950659061523680">"กลับ"</string> + <string name="dialog_close" msgid="6840699812532384661">"ปิด"</string> <string name="available" msgid="6007778121920339498">"พร้อมใช้งาน"</string> <string name="blocked" msgid="9195547604866033708">"ถูกบล็อก"</string> <string name="on" msgid="280241003226755921">"เปิด"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"ไม่มี"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(ค่าเริ่มต้นของระบบ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"ไม่มีแอป"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"บริการ NFC อื่นๆ"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"เลือกไว้"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"เลือกไว้ - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"สิทธิ์เข้าถึงพิเศษของแอป"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"เข้าถึงเมื่อวานตอน <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"เข้าถึงเมื่อวันที่ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ตอน <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"รหัสผ่านที่สามารถใช้งานได้เพียงครั้งเดียวของคุณคือ 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"อนุญาตการตั้งค่าที่จำกัด"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"การตั้งค่าที่จำกัด"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"การตั้งค่านี้ใช้ไม่ได้ในตอนนี้เพื่อความปลอดภัยของคุณ"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"การดำเนินการไม่พร้อมใช้งานขณะกำลังโทรศัพท์"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"ไม่อนุญาตให้แอปติดตั้งแอปอื่นๆ ระหว่างการโทร\n\n สแกมเมอร์มักขอการดำเนินการประเภทนี้ระหว่างการโทร ดังนั้นระบบจึงบล็อกไว้เพื่อปกป้องคุณ หากมีคนที่คุณไม่รู้จักบอกให้คุณดำเนินการดังกล่าว นั่นอาจเป็นการหลอกลวง"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"แอปถูกปฏิเสธไม่ให้เข้าถึง <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"แอปขอเข้าถึงสิทธิ์ที่มีความละเอียดอ่อนซึ่งอาจทำให้ข้อมูลส่วนบุคคลและข้อมูลทางการเงินของคุณมีความเสี่ยง<xliff:g id="ID_1"><br><br></xliff:g>แอปอาจทำงานได้ไม่ถูกต้องหากไม่มีสิทธิ์ที่จำกัดนี้ <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>ดูวิธีอนุญาตให้เข้าถึง</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"แอปถูกปฏิเสธไม่ให้เข้าถึงเพื่อเป็น<xliff:g id="ROLE_NAME">%1$s</xliff:g> เริ่มต้น"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ตกลง"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ระงับคำขอสิทธิ์อยู่"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"แอปนี้มีการขอสิทธิ์เพิ่มเติม แต่ไม่สามารถให้สิทธิ์ในเซสชันที่กำลังสตรีมอยู่ โปรดให้สิทธิ์ในโทรศัพท์ก่อน"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"สำหรับการโทรหรือส่งข้อความฉุกเฉิน"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ส่งตำแหน่งไปยังบริการช่วยเหลือฉุกเฉินแล้ว"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"แอปนี้เข้าถึงตำแหน่งของอุปกรณ์ระหว่างที่โทรหรือส่งข้อความหาหมายเลขฉุกเฉิน ซึ่งอาจเกิดขึ้นได้แม้ว่าแอปจะไม่มีสิทธิ์เข้าถึงตำแหน่งหรือแม้ว่าตำแหน่งของอุปกรณ์จะปิดอยู่ก็ตาม "<a href="https://support.google.com/android/answer/9319337">"ดูข้อมูลเพิ่มเติม"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"ตรวจสอบโทรศัพท์"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"เข้าถึงโทรศัพท์ของคุณไม่ได้"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"ลองใหม่ไหม"</string> </resources> diff --git a/PermissionController/res/values-tl-v35/strings.xml b/PermissionController/res/values-tl-v35/strings.xml new file mode 100644 index 000000000..ecd01df78 --- /dev/null +++ b/PermissionController/res/values-tl-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Seguridad"</string> +</resources> diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml index f7a2a485c..70bcec228 100644 --- a/PermissionController/res/values-tl/strings.xml +++ b/PermissionController/res/values-tl/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"mga pahintulot"</string> <string name="cancel" msgid="8943320028373963831">"Kanselahin"</string> <string name="back" msgid="6249950659061523680">"Bumalik"</string> + <string name="dialog_close" msgid="6840699812532384661">"Isara"</string> <string name="available" msgid="6007778121920339498">"Available"</string> <string name="blocked" msgid="9195547604866033708">"Naka-block"</string> <string name="on" msgid="280241003226755921">"Naka-on"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Wala"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Default ng system)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Walang app"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Iba pang serbisyo ng NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Napili"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Pinili - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Espesyal na access ng app"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Na-access kahapon nang <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Na-access noong <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ang iyong pang-isang beses na password ay 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Payagan ang mga pinaghihigpitang setting"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Pinaghihigpitang setting"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sa iyong seguridad, hindi available ang setting na ito sa ngayon."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Hindi available ang pagkilos habang may tumatawag"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Hindi pinapayagan ang pagpayag sa pag-install ng mga app ng iba pang app habang may tawag sa telepono.\n\n Madalas na humihiling ng ganitong uri ng pagkilos ang mga scammer habang tumatawag sa telepono, kaya na-block ito para protektahan ka. Kung ginagabayan kang gawin ang pagkilos na ito ng taong hindi mo kilala, posibleng scam ito."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tinanggihan ang access ng appp sa <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Humiling ang app ng access sa pahintulot sa sensitibong impormasyon na posibleng maglagay ng iyong personal at pinansyal na impormasyon sa panganib.<xliff:g id="ID_1"><br><br></xliff:g>Posibleng hindi gumana nang maayos ang app kung wala ang pinaghihigpitang pahintulot na ito. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Alamin kung paano payagan ang pag-access</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tinanggihan ang access ng app na maging default na <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Pinigilan ang kahilingan sa pahintulot"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Humihingi ang app na ito ng dagdag na pahintulot, pero hindi puwedeng ibigay ang mga pahintulot sa isang session ng streaming. Ibigay muna ang pahintulot sa iyong telepono."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para sa emergency na tawag o text"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ipinadala ang lokasyon sa mga serbisyong pang-emergency"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Na-access ng app na ito ang lokasyon ng iyong device habang may tawag o text sa isang pang-emergency na numero. Puwede itong mangyari kahit na walang pahintulot sa lokasyon ang app o naka-off ang lokasyon ng device. "<a href="https://support.google.com/android/answer/9319337">"Matuto pa"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Tingnan ang iyong telepono"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Hindi makakonekta sa iyong telepono"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Subukan ulit?"</string> </resources> diff --git a/PermissionController/res/values-tr-v35/strings.xml b/PermissionController/res/values-tr-v35/strings.xml new file mode 100644 index 000000000..ed8a1bf04 --- /dev/null +++ b/PermissionController/res/values-tr-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Güvenlik"</string> +</resources> diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml index 669471f0c..6b288f412 100644 --- a/PermissionController/res/values-tr/strings.xml +++ b/PermissionController/res/values-tr/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"izinler"</string> <string name="cancel" msgid="8943320028373963831">"İptal"</string> <string name="back" msgid="6249950659061523680">"Geri"</string> + <string name="dialog_close" msgid="6840699812532384661">"Kapat"</string> <string name="available" msgid="6007778121920339498">"İzin verildi"</string> <string name="blocked" msgid="9195547604866033708">"Engellendi"</string> <string name="on" msgid="280241003226755921">"Açık"</string> @@ -368,7 +369,7 @@ <string name="role_dialer_search_keywords" msgid="3324448983559188087">"çevirici"</string> <string name="role_sms_label" msgid="8456999857547686640">"Varsayılan SMS uygulaması"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"SMS uygulaması"</string> - <string name="role_sms_description" msgid="3424020199148153513">"Kısa mesajlar, fotoğraflar, videolar ve başka içerikler göndermek ve almak için telefon numaranızı kullanmanıza olanak tanıyan uygulamalar"</string> + <string name="role_sms_description" msgid="3424020199148153513">"Kısa mesaj, fotoğraf ve video gibi içerikleri göndermek ve almak için telefon numaranızı kullanabileceğiniz uygulamalar"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"<xliff:g id="APP_NAME">%1$s</xliff:g> varsayılan SMS uygulamanız olarak ayarlansın mı?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Bu uygulama Kamera, Kişiler, Dosyalar ve medya, Mikrofon, Telefon ve SMS\'inize erişebilecek"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"kısa mesaj, kısa mesaj gönderme, mesajlar, mesajlaşma"</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"acil durumda"</string> <string name="role_home_label" msgid="3871847846649769412">"Varsayılan ana ekran uygulaması"</string> <string name="role_home_short_label" msgid="8544733747952272337">"Ana ekran uygulaması"</string> - <string name="role_home_description" msgid="7997371519626556675">"Android cihazınızın Ana ekranı yerine geçen ve cihazınızın içeriklerine ve özelliklerine erişmenizi sağlayan, genellikle başlatıcı olarak adlandırılan uygulamalar"</string> + <string name="role_home_description" msgid="7997371519626556675">"Android cihazınızın ana ekranı yerine geçen, cihazınızın içeriklerine ve özelliklerine erişmenizi sağlayan, genellikle başlatıcılar olarak adlandırılan uygulamalar"</string> <string name="role_home_request_title" msgid="738136983453341081">"<xliff:g id="APP_NAME">%1$s</xliff:g>, varsayılan ana ekran uygulamanız olarak ayarlansın mı?"</string> <string name="role_home_request_description" msgid="2658833966716057673">"Herhangi bir izin gerekli değil"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"başlatıcı"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Yok"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Sistem varsayılanı)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Uygulama yok"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Diğer NFC hizmetleri"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Seçildi"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Seçildi - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"özel uygulama erişimi"</string> @@ -452,7 +454,7 @@ <string name="incident_report_channel_name" msgid="3144954065936288440">"Hata Ayıklama Verilerini Paylaş"</string> <string name="incident_report_notification_title" msgid="4635984625656519773">"Ayrıntılı hata ayıklama verileri paylaşılsın mı?"</string> <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g>, hata ayıklama bilgilerini yüklemek istiyor."</string> - <string name="incident_report_dialog_title" msgid="669104389325204095">"Hata ayıklama Verileri Paylaşılsın mı?"</string> + <string name="incident_report_dialog_title" msgid="669104389325204095">"Hata ayıklama verileri paylaşılsın mı?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Sistem bir sorun algıladı."</string> <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>, bu cihazda <xliff:g id="DATE">%2$s</xliff:g> saat <xliff:g id="TIME">%3$s</xliff:g> itibarıyla kaydedilen hata raporunu yüklemek istiyor. Hata raporları cihazınızla ilgili veya uygulamalar tarafından günlüğe kaydedilmiş kişisel bilgiler (örneğin kullanıcı adları, konum verisi, cihaz tanımlayıcılar ve ağ bilgileri) içerir. Hata raporlarını yalnızca bu bilgileri verme konusunda güvendiğiniz kişi ve uygulamalarla paylaşın. <xliff:g id="APP_NAME_1">%4$s</xliff:g> uygulamasının hata raporu yüklemesine izin verilsin mi?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"<xliff:g id="APP_NAME">%1$s</xliff:g> hata raporunu işlemeyle ilgili sorun oluştu. Ayrıntılı hata ayıklama verilerinin paylaşılması reddedildi. Kesinti için üzgünüz."</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Dün saat <xliff:g id="TIME_DATE">%1$s</xliff:g> itibarıyla erişildi"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Erişim tarihi: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Tek kullanımlık şifreniz 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Kısıtlanmış ayarlara izin verme"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Kısıtlanmış ayar"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvenliğiniz için bu ayar şu anda kullanılamıyor."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"İşlem, telefon görüşmesi sırasında yapılamaz"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon görüşmesi sırasında uygulamaların başka uygulama yüklemesine izin verilmez.\n\n Dolandırıcıların genellikle telefon görüşmesi sırasında talep ettiği bu tür işlemler sizi korumak için engellenir. Tanımadığınız biri tarafından bu işlemi yapmanız isteniyorsa dolandırılıyor olabilirsiniz."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Uygulamanın <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> iznine erişimi reddedildi"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Uygulama, kişisel ve finansal bilgilerinizi riske atabilecek hassas bir izne erişim istedi.<xliff:g id="ID_1"><br><br></xliff:g>Bu kısıtlı izin olmadan uygulama düzgün çalışmayabilir. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Erişime nasıl izin vereceğinizi öğrenin</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Uygulamanın varsayılan <xliff:g id="ROLE_NAME">%1$s</xliff:g> olarak kullanılma erişimi reddedildi"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Tamam"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"İzin isteği reddedildi"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu uygulama ek izinler istiyor ancak akış oturumundayken izin verilemez. Önce telefonunuzda ilgili izni verin."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Acil durum araması veya mesajı için"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Konum, acil durum hizmetlerine gönderildi"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Bu uygulama, bir acil durum numarasına telefon ederken veya mesaj gönderirken cihazınızın konumuna erişti. Bu durum, uygulamanın konum izni olmadığında veya cihazın konumu kapalı olduğunda bile yaşanabilir ."<a href="https://support.google.com/android/answer/9319337">"Daha fazla bilgi edinin"</a>"."</string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Telefonunuzu kontrol edin"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Telefonunuza ulaşılamadı"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Yeniden denemek istiyor musunuz?"</string> </resources> diff --git a/PermissionController/res/values-uk-v35/strings.xml b/PermissionController/res/values-uk-v35/strings.xml new file mode 100644 index 000000000..d6eeae9b5 --- /dev/null +++ b/PermissionController/res/values-uk-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Безпека"</string> +</resources> diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml index 53bf58314..c69790fcf 100644 --- a/PermissionController/res/values-uk/strings.xml +++ b/PermissionController/res/values-uk/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"дозволи"</string> <string name="cancel" msgid="8943320028373963831">"Скасувати"</string> <string name="back" msgid="6249950659061523680">"Назад"</string> + <string name="dialog_close" msgid="6840699812532384661">"Закрити"</string> <string name="available" msgid="6007778121920339498">"Доступно"</string> <string name="blocked" msgid="9195547604866033708">"Заблоковано"</string> <string name="on" msgid="280241003226755921">"Увімкнено"</string> @@ -194,7 +195,7 @@ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Дозволити обмежений доступ"</string> <string name="precise_image_description" msgid="6349638632303619872">"Точне місцезнаходження"</string> <string name="approximate_image_description" msgid="938803699637069884">"Приблизне місцезнаходження"</string> - <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Використовувати точне місцезнаходження"</string> + <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Точне місцезнаходження"</string> <string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Якщо вимкнено доступ до точного місцезнаходження, додатки можуть отримувати дані про приблизне"</string> <string name="app_permission_title" msgid="2090897901051370711">"Дозвіл \"<xliff:g id="PERM">%1$s</xliff:g>\""</string> <string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: доступ для цього додатка"</string> @@ -205,7 +206,7 @@ <string name="unused_apps_category_title" msgid="2988455616845243901">"Налаштування невикористовуваних додатків"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"Відкликати дозволи, якщо додаток не використовується"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Вилучати дозволи й звільняти місце"</string> - <string name="unused_apps_label_v2" msgid="7058776770056517980">"Призупиняти роботу в неактивний період"</string> + <string name="unused_apps_label_v2" msgid="7058776770056517980">"Призупиняти, коли додаток неактивний"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Керувати невикористовуваним додатком"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Вилучити дозволи, видалити тимчасові файли й зупинити сповіщення"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Вилучити дозволи, видалити тимчасові файли, зупинити сповіщення й архівувати додаток"</string> @@ -361,7 +362,7 @@ <string name="role_browser_request_title" msgid="2895200507835937192">"Зробити <xliff:g id="APP_NAME">%1$s</xliff:g> веб-переглядачем за умовчанням?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"Дозволи не потрібні"</string> <string name="role_dialer_label" msgid="1100224146343237968">"Дзвінки за умовчанням"</string> - <string name="role_dialer_short_label" msgid="7186888549465352489">"Додаток для викликів"</string> + <string name="role_dialer_short_label" msgid="7186888549465352489">"Додаток для дзвінків"</string> <string name="role_dialer_description" msgid="8768708633696539612">"Додатки, у яких можна здійснювати й приймати виклики на пристрої"</string> <string name="role_dialer_request_title" msgid="5959618560705912058">"Чи має <xliff:g id="APP_NAME">%1$s</xliff:g> використовуватись як додаток для викликів за умовчанням?"</string> <string name="role_dialer_request_description" msgid="6288839625724909320">"Цьому додатку буде надано дозволи \"Камера\", \"Контакти\", \"Мікрофон\", \"Телефон\" і \"SMS\""</string> @@ -380,7 +381,7 @@ <string name="role_emergency_search_keywords" msgid="1920007722599213358">"в екстреному випадку"</string> <string name="role_home_label" msgid="3871847846649769412">"Додаток головного екрана за умовчанням"</string> <string name="role_home_short_label" msgid="8544733747952272337">"Додаток головного екрана"</string> - <string name="role_home_description" msgid="7997371519626556675">"Додатки, які заміняють головний екран і забезпечують доступ до вмісту та функцій пристрою Android (так звані панелі запуску)"</string> + <string name="role_home_description" msgid="7997371519626556675">"Додатки, які заміняють головний екран і забезпечують доступ до вмісту й функцій пристрою Android (так звані панелі запуску)"</string> <string name="role_home_request_title" msgid="738136983453341081">"Чи має <xliff:g id="APP_NAME">%1$s</xliff:g> використовуватись як додаток головного екрана за умовчанням?"</string> <string name="role_home_request_description" msgid="2658833966716057673">"Дозволи не потрібні"</string> <string name="role_home_search_keywords" msgid="3830755001192666285">"панель запуску"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Немає"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(За умовчанням)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Немає додатків"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Інші NFC-сервіси"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Вибрано"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Вибрано: <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"спеціальний доступ додатка"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> хоче завантажити інформацію про налагодження."</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"Поділитися даними про налагодження?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"Система виявила проблему."</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> запитує, чи можна завантажити повідомлення про помилку, створене на цьому пристрої <xliff:g id="TIME">%3$s</xliff:g> <xliff:g id="DATE">%2$s</xliff:g>. Повідомлення про помилки містять особисту інформацію про ваш пристрій або відомості, записані додатками, як-от імена користувачів, місцеположення, ідентифікатори пристроїв та інформацію про мережу. Діліться повідомленнями про помилки лише з тими людьми й додатками, яким довіряєте. Дозволити додатку <xliff:g id="APP_NAME_1">%4$s</xliff:g> завантажити повідомлення про помилку?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> запитує, чи можна завантажити повідомлення про помилку, створене на цьому пристрої <xliff:g id="DATE">%2$s</xliff:g> о <xliff:g id="TIME">%3$s</xliff:g>. Повідомлення про помилки містять особисту інформацію про ваш пристрій або відомості, записані додатками, як-от імена користувачів, місцеположення, ідентифікатори пристроїв та інформацію про мережу. Діліться повідомленнями про помилки лише з тими людьми й додатками, яким довіряєте. Дозволити додатку <xliff:g id="APP_NAME_1">%4$s</xliff:g> завантажити повідомлення про помилку?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"Не вдалося обробити повідомлення про помилку в додатку <xliff:g id="APP_NAME">%1$s</xliff:g>. Дані про налагодження не надіслано. Вибачте за незручності."</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Дозволити"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Заборонити"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Доступ отримано вчора: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Доступ отримано: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Ваш одноразовий пароль: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Дозволити налаштування з обмеженнями"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налаштування з обмеженнями"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"З міркувань безпеки це налаштування наразі недоступне."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Дія недоступна під час телефонного виклику"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Під час телефонного виклику не можна надавати додаткам дозвіл на встановлення інших додатків.\n\n Шахраї часто просять виконати такі дії під час телефонних розмов, тому цю можливість заблоковано задля вашого захисту. Якщо незнайома особа просить вас виконати таку дію, це може бути шахрайством."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Додатку не надано дозвіл \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Додаток запитав дозвіл на доступ до чутливих даних, що може поставити під загрозу вашу особисту й фінансову інформацію.<xliff:g id="ID_1"><br><br></xliff:g>Без цього обмеженого дозволу додаток може не працювати належним чином. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Як надати доступ</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Додатку не надано доступ до ролі \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\" за умовчанням"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запит на доступ відхилено"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Цьому додатку потрібно більше дозволів, але їх не можна надати під час потокового передавання. Спершу надайте дозвіл на телефоні."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Для екстрених викликів або SMS"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Геодані надіслано службам екстреної допомоги"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Цей додаток отримав доступ до геоданих вашого пристрою, коли ви телефонували або надсилали SMS на екстрений номер. Це може статися, навіть якщо додаток не має дозволу на доступ до геоданих або коли на пристрої вимкнено геолокацію. "<a href="https://support.google.com/android/answer/9319337">"Докладніше"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Перевірте телефон"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Не вдалося підключитися до телефона"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Повторити?"</string> </resources> diff --git a/PermissionController/res/values-ur-v35/strings.xml b/PermissionController/res/values-ur-v35/strings.xml new file mode 100644 index 000000000..d06432a1e --- /dev/null +++ b/PermissionController/res/values-ur-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"سیکیورٹی"</string> +</resources> diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml index 8927febb3..cc9182c1e 100644 --- a/PermissionController/res/values-ur/strings.xml +++ b/PermissionController/res/values-ur/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"اجازتیں"</string> <string name="cancel" msgid="8943320028373963831">"منسوخ کریں"</string> <string name="back" msgid="6249950659061523680">"واپس جائیں"</string> + <string name="dialog_close" msgid="6840699812532384661">"بند کریں"</string> <string name="available" msgid="6007778121920339498">"دستیاب ہے"</string> <string name="blocked" msgid="9195547604866033708">"مسدود کردہ"</string> <string name="on" msgid="280241003226755921">"آن"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"کوئی نہیں"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(سسٹم ڈیفالٹ)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"کوئی ایپس نہیں ہیں"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"دیگر NFC سروسز"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"منتخب کردہ"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"منتخب کردہ - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ایپ کی خاص رسائی"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"گزشتہ کل <xliff:g id="TIME_DATE">%1$s</xliff:g> پر رسائی حاصل کی گئی"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> پر رسائی حاصل کی گئی"</string> <string name="test_otp_msg" msgid="7559110574222727550">"آپ کا ایک وقتی پاس ورڈ 132435 ہے"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"پابندی والی ترتیبات کی اجازت دیں"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"محدود ترتیب"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"آپ کی سیکیورٹی کیلئے، یہ ترتیب فی الحال دستیاب نہیں ہے۔"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"فون کال کے دوران کارروائی دستیاب نہیں ہے"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"فون کال کے دوران ایپس کو دوسری ایپس انسٹال کرنے کی اجازت دینا ممنوع ہے۔\n\n دھوکے باز اکثر فون کال گفتگوؤں کے دوران اس قسم کی کارروائی کی درخواست کرتے ہیں، اس لیے اسے آپ کی حفاظت کے لیے مسدود کر دیا جاتا ہے۔ اگر آپ کو کسی ایسے شخص کے ذریعے سے یہ کارروائی کرنے کے لیے رہنمائی کی جا رہی ہے جس کو آپ نہیں جانتے ہیں تو یہ ایک فریب کاری ہو سکتی ہے۔"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> تک ایپ کی رسائی کو مسترد کر دیا گیا"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ایپ نے ایک حساس اجازت تک رسائی کی درخواست کی ہے جو آپ کی ذاتی اور مالی معلومات کو خطرے میں ڈال سکتی ہے۔<xliff:g id="ID_1"><br><br></xliff:g>یہ ممکن ہے کہ اس محدود اجازت کے بغیر ایپ ٹھیک سے کام نہ کرے۔ <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>رسائی کی اجازت دینے کا طریقہ جانیں</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ڈیفالٹ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ہونے کے لیے ایپ کی رسائی کو مسترد کر دیا گیا"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ٹھیک ہے"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"اجازت کی درخواست مسترد کر دی گئی"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"یہ ایپ اضافی اجازتوں کی درخواست کر رہی ہے، لیکن سلسلہ بندی کے سیشن میں اجازتیں نہیں دی جا سکتیں۔ پہلے اپنے فون پر اجازت دیں۔"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ایمرجنسی کال یا ٹیکسٹ کے لیے"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ایمرجنسی سروسز کو مقام بھیج دیا گیا"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"اس ایپ نے ایمرجنسی نمبر پر ٹیکسٹ یا کال کے دوران آپ کے آلہ کے مقام تک رسائی حاصل کی۔ ایسا تب بھی ہو سکتا ہے جب ایپ کو مقام کی اجازت نہ ہو یا آلہ کا مقام آف ہو۔ "<a href="https://support.google.com/android/answer/9319337">"مزید جانیں"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"اپنا فون چیک کریں"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"آپ کے فون سے منسلک نہیں کیا جا سکا"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"دوبارہ کوشش کریں؟"</string> </resources> diff --git a/PermissionController/res/values-uz-v35/strings.xml b/PermissionController/res/values-uz-v35/strings.xml new file mode 100644 index 000000000..965903b10 --- /dev/null +++ b/PermissionController/res/values-uz-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Xavfsizlik"</string> +</resources> diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml index 5ba4ce531..55a271e39 100644 --- a/PermissionController/res/values-uz/strings.xml +++ b/PermissionController/res/values-uz/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"ruxsatlar"</string> <string name="cancel" msgid="8943320028373963831">"Bekor qilish"</string> <string name="back" msgid="6249950659061523680">"Orqaga"</string> + <string name="dialog_close" msgid="6840699812532384661">"Yopish"</string> <string name="available" msgid="6007778121920339498">"Mavjud"</string> <string name="blocked" msgid="9195547604866033708">"Bloklangan"</string> <string name="on" msgid="280241003226755921">"Yoniq"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Hech qanday"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Birlamchi)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Hech qanday ilova topilmadi"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Boshqa NFC xizmatlari"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Tanlandi"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Tanlandi – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"maxsus ilova ruxsatlari"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kecha kirilgan: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kirilgan: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Bir martalik parolingiz: 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Cheklangan sozlamalarga ruxsat berish"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cheklangan sozlama"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Xavfsizlik maqsadida bu sozlama hozir ishlamaydi."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Telefon chaqiruvi paytida amal ishlamaydi"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Telefon chaqiruvi paytida ilovalarga boshqa ilovalarni oʻrnatishga ruxsat berish mumkin emas.\n\n Tovlamachilar koʻpincha telefon chaqiruvi orqali suhbatlar paytida shu turdagi amalni bajarishni soʻraydi, shu bois u sizni himoyalash uchun bloklanadi. Sizga notanish biror kishi shu amalni bajarishga undasa, bu tovlamachilik boʻlishi mumkin."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ilovaga <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ruxsati berilmadi"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ilova maxfiy maʼlumotlarga kirish uchun ruxsat soʻramoqda. Ruxsat bersangiz, shaxsiy va moliyaviy maʼlumotlaringiz xavf ostida qolishi mumkin.<xliff:g id="ID_1"><br><br></xliff:g> Bu cheklangan ruxsatsiz ilova toʻgʻri ishlamasligi mumkin. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Qanday ruxsat berish haqida batafsil</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ilovadan <xliff:g id="ROLE_NAME">%1$s</xliff:g> turkumidagi standart sifatida foydalanish taqiqlangan"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Ruxsat talabi bloklandi"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu ilova qoʻshimcha ruxsatlar talab qilmoqda, lekin ruxsatlar striming seansida berilmaydi. Avval telefoningizda ruxsat bering."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Favqulodda chaqiruv yoki SMS uchun"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Joylashuv axboroti favqulodda xizmatlarga yuborildi"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Bu ilova favqulodda xizmat raqamini chaqirish yoki SMS yuborish vaqtida qurilmangiz joylashuv axborotidan foydalandi. Bu ilovaga joylashuv axboroti uchun ruxsat berilmagan yoki qurilmada joylashuv xizmati faolsizlantirilgan boʻlsa ham yuz berishi mumkin. "<a href="https://support.google.com/android/answer/9319337">"Batafsil"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Telefonga qarang"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Telefonga ulanmadi"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Qayta urinib koʻrasizmi?"</string> </resources> diff --git a/PermissionController/res/values-v31/config.xml b/PermissionController/res/values-v31/config.xml index 2b8817d98..3f286fa98 100644 --- a/PermissionController/res/values-v31/config.xml +++ b/PermissionController/res/values-v31/config.xml @@ -17,5 +17,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Name of a font family to use for headlines in SettingsLib. This overrides the default value which points to a private resource. Setting it to an empty value allows it to fallback to the default on the device. --> <string name="settingslib_config_headlineFontFamily" translatable="false">@string/config_headlineFontFamily</string> + + <!-- Changing this config on platform build sdk 30 and earlier does nothing. --> + <bool name="config_useCollapsingToolbarInPermissionSettings">true</bool> </resources> diff --git a/PermissionController/res/values-v31/styles.xml b/PermissionController/res/values-v31/styles.xml index d48012894..20fb84701 100644 --- a/PermissionController/res/values-v31/styles.xml +++ b/PermissionController/res/values-v31/styles.xml @@ -16,6 +16,23 @@ <resources> + <!-- START AUTO PERMISSION GRANT DIALOG --> + + <style name="AutoPermissionGrantButton" parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> + <item name="android:layout_width">match_parent</item> + <item name="android:minHeight">@dimen/car_location_permission_prompt_min_height</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginTop">@dimen/car_location_permission_prompt_margin_small</item> + <item name="android:padding">@dimen/car_location_permission_prompt_padding_extra_small</item> + <item name="android:textSize">@dimen/car_location_permission_prompt_button_text_size</item> + <item name="android:lineHeight">@dimen/car_location_permission_prompt_line_height_small</item> + <item name="android:textColor">@android:color/system_accent2_900</item> + <item name="android:background">@drawable/auto_grant_permissions_buttons</item> + <item name="android:textAppearance">@style/AutoPermissionGrantTitleMessage</item> + </style> + + <!-- END AUTO PERMISSION GRANT DIALOG --> + <!-- START PERMISSION GRANT DIALOG --> <style name="PermissionGrantDialogMaterial3"> @@ -44,7 +61,8 @@ <style name="PermissionGrantButtonTop" parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">56dp</item> + <item name="android:minHeight">56dp</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginLeft">24dp</item> <item name="android:layout_marginRight">24dp</item> <item name="android:layout_marginTop">2dp</item> @@ -58,7 +76,8 @@ <style name="PermissionGrantButtonMiddle" parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">56dp</item> + <item name="android:minHeight">56dp</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginLeft">24dp</item> <item name="android:layout_marginRight">24dp</item> <item name="android:layout_marginTop">2dp</item> @@ -72,7 +91,8 @@ <style name="PermissionGrantButtonBottom" parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">56dp</item> + <item name="android:minHeight">56dp</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginLeft">24dp</item> <item name="android:layout_marginRight">24dp</item> <item name="android:layout_marginTop">2dp</item> diff --git a/PermissionController/res/values-v33/attrs.xml b/PermissionController/res/values-v33/attrs.xml index 4a417076d..b136ab718 100644 --- a/PermissionController/res/values-v33/attrs.xml +++ b/PermissionController/res/values-v33/attrs.xml @@ -39,8 +39,9 @@ <attr name="scStatusButtonStyle" format="reference" /> <attr name="scActionButtonListLayout" format="reference" /> <attr name="scActionButtonTheme" format="reference" /> - <attr name="scActionButtonStyle" format="reference"/> - <attr name="scSecondaryActionButtonStyle" format="reference"/> + <attr name="scActionButtonStyle" format="reference" /> + <attr name="scSecondaryActionButtonStyle" format="reference" /> + <attr name="scCardSideMargin" format="dimension" /> <attr name="colorScShieldAccent" format="color" /> </resources>
\ No newline at end of file diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml index 94344615b..7e959c2f4 100644 --- a/PermissionController/res/values-v33/styles.xml +++ b/PermissionController/res/values-v33/styles.xml @@ -317,8 +317,8 @@ <item name="android:paddingEnd">@dimen/sc_spacing_xxxlarge</item> <item name="android:paddingTop">@dimen/sc_spacing_xxxlarge</item> <item name="android:paddingBottom">@dimen/sc_card_margin_bottom</item> - <item name="android:layout_marginStart">@dimen/sc_spacing_large</item> - <item name="android:layout_marginEnd">@dimen/sc_spacing_large</item> + <item name="android:layout_marginStart">?attr/scCardSideMargin</item> + <item name="android:layout_marginEnd">?attr/scCardSideMargin</item> <item name="android:background">@drawable/safety_center_card_background</item> </style> diff --git a/PermissionController/res/values-v33/themes.xml b/PermissionController/res/values-v33/themes.xml index 82a8ef5b6..a3d1b16c3 100644 --- a/PermissionController/res/values-v33/themes.xml +++ b/PermissionController/res/values-v33/themes.xml @@ -57,6 +57,8 @@ @style/SecondarySafetyCenterActionButton.Fixed </item> + <item name="scCardSideMargin">@dimen/sc_spacing_large</item> + <item name="textColorScActionButton">@color/sc_primary_action_button_text</item> <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item> @@ -69,6 +71,10 @@ <style name="Theme.SafetyCenterQs" parent="Theme.SafetyCenterQsBase" /> + <style name="Theme.SafetyCenterQsExpressive" parent="Theme.SafetyCenterQs"> + <item name="scCardSideMargin">0dp</item> + </style> + <style name="Theme.SafetyCenterBase" parent="Theme.PermissionController.Settings.FilterTouches"> <item name="colorSurface">@color/sc_surface_light</item> <item name="colorSurfaceVariant">@color/sc_surface_variant_light</item> @@ -104,6 +110,8 @@ @style/SecondarySafetyCenterActionButton.Responsive </item> + <item name="scCardSideMargin">@dimen/sc_spacing_large</item> + <item name="textColorScActionButton">@color/sc_primary_action_button_text</item> <item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item> @@ -115,4 +123,8 @@ </style> <style name="Theme.SafetyCenter" parent="Theme.SafetyCenterBase" /> + + <style name="Theme.SafetyCenterExpressive" parent="Theme.SafetyCenter"> + <item name="scCardSideMargin">0dp</item> + </style> </resources>
\ No newline at end of file diff --git a/PermissionController/res/values-v35/attrs.xml b/PermissionController/res/values-v35/attrs.xml new file mode 100644 index 000000000..701534733 --- /dev/null +++ b/PermissionController/res/values-v35/attrs.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<resources> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set + when a preference has an icon or has its icon space reserved. --> + <attr name="state_has_icon_space" format="boolean" /> +</resources> diff --git a/PermissionController/res/values-v35/strings.xml b/PermissionController/res/values-v35/strings.xml new file mode 100644 index 000000000..27a50bc0d --- /dev/null +++ b/PermissionController/res/values-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Title for a section that contains security settings on the Security and Privacy page [CHAR LIMIT=60] --> + <string name="safety_center_entries_category_title">Security</string> +</resources> diff --git a/PermissionController/res/values-v35/styles.xml b/PermissionController/res/values-v35/styles.xml new file mode 100644 index 000000000..513754bb8 --- /dev/null +++ b/PermissionController/res/values-v35/styles.xml @@ -0,0 +1,417 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<resources> + + <!-- START PREFERENCE STYLES --> + + <style name="PermissionPreferenceCategoryRootLayoutStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingLeft</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + <item name="android:paddingRight">?android:attr/listPreferredItemPaddingRight</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:baselineAligned">false</item> + <item name="android:layout_marginTop">16dp</item> + <item name="android:gravity">center_vertical</item> + </style> + + <style name="PermissionPreferenceCategoryTextRelativeLayoutStyle"> + <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:paddingTop">8dp</item> + <item name="android:paddingBottom">8dp</item> + </style> + + <style name="PermissionPreferenceCategoryTitleTextStyle" parent="@style/PreferenceCategoryTitleTextStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + </style> + + <style name="PermissionPreferenceCategorySummaryTextStyle" style="@style/PreferenceSummaryTextStyle"> + <item name="android:ellipsize">end</item> + <item name="android:singleLine">true</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_below">@android:id/title</item> + <item name="android:layout_alignLeft">@android:id/title</item> + <item name="android:layout_alignStart">@android:id/title</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:maxLines">10</item> + </style> + + <style name="PermissionPreferenceRootLayoutStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="android:gravity">center_vertical</item> + <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingLeft</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + <item name="android:paddingRight">?android:attr/listPreferredItemPaddingRight</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:clipToPadding">false</item> + <item name="android:baselineAligned">false</item> + </style> + + <style name="PermissionPreferenceIconFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minWidth">48dp</item> + <item name="android:gravity">start|center_vertical</item> + <item name="android:orientation">horizontal</item> + <item name="android:paddingLeft">0dp</item> + <item name="android:paddingStart">0dp</item> + <item name="android:paddingRight">8dp</item> + <item name="android:paddingEnd">8dp</item> + <item name="android:paddingTop">4dp</item> + <item name="android:paddingBottom">4dp</item> + </style> + + <style name="PermissionPreferenceIconStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="maxWidth">48dp</item> + <item name="maxHeight">48dp</item> + </style> + + <style name="PermissionPreferenceTextRelativeLayoutStyle"> + <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:paddingTop">16dp</item> + <item name="android:paddingBottom">16dp</item> + </style> + + <style name="PermissionPreferenceTitleTextStyle" > + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:maxLines">2</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:textAppearance">?android:attr/textAppearanceListItem</item> + <item name="android:ellipsize">marquee</item> + </style> + + <style name="PermissionPreferenceSummaryTextStyle" parent="PreferenceSummaryTextStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_below">@android:id/title</item> + <item name="android:layout_alignLeft">@android:id/title</item> + <item name="android:layout_alignStart">@android:id/title</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:maxLines">10</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + </style> + + <style name="PermissionPreferenceWidgetFrameLayoutStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:gravity">end|center_vertical</item> + <item name="android:paddingLeft">16dp</item> + <item name="android:paddingStart">16dp</item> + <item name="android:paddingRight">0dp</item> + <item name="android:paddingEnd">0dp</item> + <item name="android:orientation">vertical</item> + </style> + + <style name="PermissionFooterPreferenceRootLayoutStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeight</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:orientation">vertical</item> + <item name="android:clipToPadding">false</item> + </style> + + <style name="PermissionFooterPreferenceIconFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minWidth">56dp</item> + <item name="android:gravity">start|top</item> + <item name="android:orientation">horizontal</item> + <item name="android:paddingEnd">12dp</item> + <item name="android:paddingTop">16dp</item> + <item name="android:paddingBottom">4dp</item> + </style> + + <style name="PermissionFooterPreferenceIconStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + </style> + + <style name="PermissionFooterPreferenceLearnMoreTextLayoutStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + </style> + + <style name="PermissionFooterPreferenceTitleTextStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:paddingTop">16dp</item> + <item name="android:paddingBottom">8dp</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:ellipsize">marquee</item> + </style> + + <style name="PermissionFooterPreferenceLearnMoreTextStyle" parent="TextAppearance.Footer.Title.SettingsLib"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">start</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:paddingBottom">8dp</item> + <item name="android:clickable">true</item> + <item name="android:visibility">gone</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceRootLayoutStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceWidgetFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:paddingHorizontal">20dp</item> + <item name="android:gravity">center</item> + <item name="android:minWidth">56dp</item> + <item name="android:orientation">vertical</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceIconFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minWidth">32dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:layout_marginEnd">16dp</item> + <item name="android:paddingTop">4dp</item> + <item name="android:paddingBottom">4dp</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceIconStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="maxWidth">@dimen/secondary_app_icon_size</item> + <item name="maxHeight">@dimen/secondary_app_icon_size</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceTextContainerStyle"> + <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:orientation">vertical</item> + <item name="android:paddingTop">16dp</item> + <item name="android:paddingBottom">16dp</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceTitleStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:maxLines">2</item> + <item name="android:ellipsize">end</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:textAppearance">?android:attr/textAppearanceListItem</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceSummaryContainerStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:visibility">gone</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceSummaryStyle"> + <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:textAppearance">?android:attr/textAppearanceSmall</item> + <item name="android:textAlignment">viewStart</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceAppendixStyle"> + <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:textAppearance">?android:attr/textAppearanceSmall</item> + <item name="android:textAlignment">viewEnd</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:maxLines">1</item> + <item name="android:visibility">gone</item> + <item name="android:ellipsize">end</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:orientation">horizontal</item> + <item name="android:gravity">center_vertical</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle"> + <item name="android:layout_width">.75dp</item> + <item name="android:layout_height">32dp</item> + <item name="android:layout_marginTop">16dp</item> + <item name="android:layout_marginBottom">16dp</item> + <item name="android:background">?android:attr/dividerVertical</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:minWidth">@dimen/two_target_min_width</item> + <item name="android:layout_height">fill_parent</item> + <item name="android:src">@drawable/ic_settings_accent</item> + <item name="android:paddingStart">24dp</item> + <item name="android:paddingEnd">24dp</item> + <item name="android:layout_gravity">center</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + + <style name="PermissionSelectorWithWidgetPreferenceWidgetRadioButton"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center</item> + <item name="android:background">@null</item> + <item name="android:focusable">false</item> + <item name="android:clickable">false</item> + </style> + + <style name="PermissionTwoTargetPreferenceRootLayoutStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="android:gravity">center_vertical</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:clipToPadding">false</item> + </style> + + <style name="PermissionTwoTargetPreferenceTextContainerStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_weight">1</item> + <item name="android:paddingTop">16dp</item> + <item name="android:paddingBottom">16dp</item> + </style> + + <style name="PermissionTwoTargetPreferenceTitleStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:maxLines">2</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:textAppearance">?android:attr/textAppearanceListItem</item> + <item name="android:ellipsize">marquee</item> + </style> + + <style name="PermissionTwoTargetPreferenceSummaryStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_below">@android:id/title</item> + <item name="android:layout_alignStart">@android:id/title</item> + <item name="android:textAppearance">?android:attr/textAppearanceListItemSecondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:hyphenationFrequency">normalFast</item> + <item name="android:lineBreakWordStyle">phrase</item> + <item name="android:maxLines">10</item> + </style> + + <style name="PermissionTwoTargetPreferenceWidgetFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:minWidth">@dimen/two_target_min_width</item> + <item name="android:gravity">center</item> + <item name="android:orientation">vertical</item> + </style> + + <style name="PermissionTwoTargetPreferenceIconFrameStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:minWidth">48dp</item> + <item name="android:gravity">start|center_vertical</item> + <item name="android:orientation">horizontal</item> + <item name="android:paddingLeft">0dp</item> + <item name="android:paddingStart">0dp</item> + <item name="android:paddingRight">8dp</item> + <item name="android:paddingEnd">8dp</item> + <item name="android:paddingTop">4dp</item> + <item name="android:paddingBottom">4dp</item> + </style> + + <style name="PermissionTwoTargetPreferenceIconStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="maxWidth">48dp</item> + <item name="maxHeight">48dp</item> + </style> + + <style name="PermissionTwoTargetPreferenceDividerContainerStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:gravity">start|center_vertical</item> + <item name="android:orientation">horizontal</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:paddingTop">16dp</item> + <item name="android:paddingBottom">16dp</item> + </style> + + <style name="PermissionTwoTargetPreferenceDividerStyle"> + <item name="android:layout_width">1dp</item> + <item name="android:layout_height">32dp</item> + <item name="android:background">?android:attr/listDivider</item> + </style> + + <style name="AppPermissionFooterLinkPreferenceRootLayoutStyle" + parent="PermissionPreferenceRootLayoutStyle" /> + + <style name="AppPermissionFooterLinkPreferenceTextLayoutStyle" + parent="PermissionPreferenceTextRelativeLayoutStyle" /> + + <style name="AppPermissionFooterLinkPreferenceSummaryStyle" + parent="PermissionPreferenceSummaryTextStyle" /> + + <!-- END PREFERENCE STYLES --> +</resources>
\ No newline at end of file diff --git a/PermissionController/res/values-v35/themes.xml b/PermissionController/res/values-v35/themes.xml index 49da01c6c..ff6c430c4 100644 --- a/PermissionController/res/values-v35/themes.xml +++ b/PermissionController/res/values-v35/themes.xml @@ -31,4 +31,5 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> + <style name="ThemeOverlay.PermissionSettings" parent="" /> </resources> diff --git a/PermissionController/res/values-vi-v35/strings.xml b/PermissionController/res/values-vi-v35/strings.xml new file mode 100644 index 000000000..56c9bd351 --- /dev/null +++ b/PermissionController/res/values-vi-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Bảo mật"</string> +</resources> diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml index 3796b4f9d..ecb94368c 100644 --- a/PermissionController/res/values-vi/strings.xml +++ b/PermissionController/res/values-vi/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"quyền"</string> <string name="cancel" msgid="8943320028373963831">"Hủy"</string> <string name="back" msgid="6249950659061523680">"Quay lại"</string> + <string name="dialog_close" msgid="6840699812532384661">"Đóng"</string> <string name="available" msgid="6007778121920339498">"Được phép"</string> <string name="blocked" msgid="9195547604866033708">"Bị chặn"</string> <string name="on" msgid="280241003226755921">"Đang bật"</string> @@ -205,7 +206,7 @@ <string name="unused_apps_category_title" msgid="2988455616845243901">"Chế độ cài đặt cho ứng dụng không dùng đến"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"Thu hồi quyền nếu bạn không dùng ứng dụng"</string> <string name="unused_apps_label" msgid="2595428768404901064">"Thu hồi quyền và giải phóng dung lượng"</string> - <string name="unused_apps_label_v2" msgid="7058776770056517980">"Tạm dừng hoạt động của ứng dụng nếu không dùng"</string> + <string name="unused_apps_label_v2" msgid="7058776770056517980">"Dừng hoạt động ứng dụng nếu không dùng"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"Quản lý ứng dụng nếu không dùng"</string> <string name="unused_apps_summary" msgid="8839466950318403115">"Loại bỏ quyền, xoá tệp tạm thời và dừng thông báo"</string> <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Loại bỏ quyền, xoá tệp tạm thời, dừng thông báo và lưu trữ ứng dụng"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Không có"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Ứng dụng mặc định của hệ thống)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Không có ứng dụng nào"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Dịch vụ NFC khác"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Đã chọn"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Đã chọn – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"quyền truy cập đặc biệt"</string> @@ -507,7 +509,7 @@ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ghi âm?"</string> <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ghi âm trên <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ứng dụng này có thể muốn ghi âm mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Cho phép trong phần cài đặt."</annotation></string> - <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Thay đổi quyền sử dụng micrô của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> + <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Thay đổi quyền truy cập micrô của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>?"</string> <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Thay đổi quyền truy cập vào micrô của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> trên <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ứng dụng này muốn ghi âm mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Cho phép trong phần cài đặt."</annotation></string> <string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào dữ liệu hoạt động thể chất?"</string> @@ -543,7 +545,7 @@ <string name="not_used_permissions_description" msgid="7595514824169388718">"Quyền mà chỉ có ứng dụng hệ thống sử dụng."</string> <string name="additional_permissions_label" msgid="7693557637462569046">"Quyền bổ sung"</string> <string name="additional_permissions_description" msgid="2186611950890732112">"Quyền do ứng dụng xác định."</string> - <string name="privdash_label_camera" msgid="1426440033626198096">"Máy ảnh"</string> + <string name="privdash_label_camera" msgid="1426440033626198096">"Camera"</string> <string name="privdash_label_microphone" msgid="8415035835803511693">"Micrô"</string> <string name="privdash_label_location" msgid="6882400763866489291">"Vị trí"</string> <string name="privdash_label_other" msgid="3710394147423236033">"Khác"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Truy cập hôm qua lúc <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Truy cập lúc <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ngày <xliff:g id="TIME_DATE_0">%1$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Mật khẩu một lần của bạn là 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Cho phép các chế độ cài đặt bị hạn chế"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Chế độ cài đặt bị hạn chế"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Để đảm bảo an toàn cho bạn, chế độ cài đặt này hiện không dùng được."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Không thao tác được khi đang gọi điện"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Không cho phép ứng dụng cài đặt các ứng dụng khác trong khi bạn gọi điện.\n\n Kẻ lừa đảo thường yêu cầu bạn cài đặt ứng dụng trong khi gọi điện, vì vậy, hành động này bị chặn để bảo vệ bạn. Nếu người mà bạn không biết hướng dẫn bạn cài đặt ứng dụng, thì đó có thể là thủ đoạn lừa đảo."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ứng dụng đã bị từ chối cấp quyền truy cập vào <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ứng dụng này yêu cầu quyền truy cập vào thông tin nhạy cảm. Việc đó có thể khiến thông tin cá nhân và thông tin tài chính của bạn gặp rủi ro.<xliff:g id="ID_1"><br><br></xliff:g>Có thể ứng dụng sẽ hoạt động không đúng cách nếu không được cấp quyền bị hạn chế này. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Tìm hiểu cách cấp quyền</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ứng dụng đã bị từ chối cấp quyền để làm <xliff:g id="ROLE_NAME">%1$s</xliff:g> mặc định"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Yêu cầu quyền đã bị chặn"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ứng dụng này đang yêu cầu thêm quyền, nhưng bạn không thể cấp quyền trong một phiên truyền trực tuyến. Trước tiên, hãy cấp quyền trên điện thoại của bạn."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Đối với cuộc gọi hoặc tin nhắn khẩn cấp"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Thông tin vị trí được gửi đến dịch vụ khẩn cấp"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ứng dụng này đã truy cập thông tin vị trí thiết bị của bạn trong khi gọi điện hoặc nhắn tin tới một số khẩn cấp. Điều này có thể xảy ra ngay cả khi ứng dụng này không có quyền truy cập thông tin vị trí hoặc dịch vụ vị trí trên thiết bị đang tắt. "<a href="https://support.google.com/android/answer/9319337">"Tìm hiểu thêm"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Kiểm tra điện thoại của bạn"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Không kết nối được với điện thoại của bạn"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Thử lại?"</string> </resources> diff --git a/PermissionController/res/values-watch/donottranslate.xml b/PermissionController/res/values-watch/donottranslate.xml index c3ab3cbb1..43830a93c 100644 --- a/PermissionController/res/values-watch/donottranslate.xml +++ b/PermissionController/res/values-watch/donottranslate.xml @@ -28,4 +28,55 @@ <string name="wear_material_compose_caption_1_font_family">font-family-text-medium-device-default</string> <string name="wear_material_compose_caption_2_font_family">font-family-text-medium-device-default</string> <string name="wear_material_compose_caption_3_font_family">font-family-text-medium-device-default</string> + + <string name="wear_compose_material3_arc_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_arc_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_arc_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_body_extra_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_body_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_body_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_body_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_display_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_display_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_display_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_label_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_label_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_label_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_numeral_extra_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_numeral_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_numeral_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_numeral_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_numeral_extra_large_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_title_small_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_title_medium_font_family">font-family-flex-device-default</string> + <string name="wear_compose_material3_title_large_font_family">font-family-flex-device-default</string> + + <dimen name="wear_compose_material3_arc_small_font_size">14sp</dimen> + <dimen name="wear_compose_material3_arc_medium_font_size">15sp</dimen> + <dimen name="wear_compose_material3_arc_large_font_size">20sp</dimen> + <dimen name="wear_compose_material3_body_extra_small_font_size">10sp</dimen> + <dimen name="wear_compose_material3_body_small_font_size">12sp</dimen> + <dimen name="wear_compose_material3_body_medium_font_size">14sp</dimen> + <dimen name="wear_compose_material3_body_large_font_size">16sp</dimen> + <dimen name="wear_compose_material3_display_small_font_size">24sp</dimen> + <dimen name="wear_compose_material3_display_medium_font_size">30sp</dimen> + <dimen name="wear_compose_material3_display_large_font_size">40sp</dimen> + <dimen name="wear_compose_material3_label_small_font_size">13sp</dimen> + <dimen name="wear_compose_material3_label_medium_font_size">15sp</dimen> + <dimen name="wear_compose_material3_label_large_font_size">20sp</dimen> + <dimen name="wear_compose_material3_numeral_extra_small_font_size">24sp</dimen> + <dimen name="wear_compose_material3_numeral_small_font_size">30sp</dimen> + <dimen name="wear_compose_material3_numeral_medium_font_size">40sp</dimen> + <dimen name="wear_compose_material3_numeral_large_font_size">50sp</dimen> + <dimen name="wear_compose_material3_numeral_extra_large_font_size">60sp</dimen> + <dimen name="wear_compose_material3_title_small_font_size">14sp</dimen> + <dimen name="wear_compose_material3_title_medium_font_size">16sp</dimen> + <dimen name="wear_compose_material3_title_large_font_size">20sp</dimen> + + <dimen name="wear_compose_material3_shape_corner_extra_small_size">4dp</dimen> + <dimen name="wear_compose_material3_shape_corner_small_size">8dp</dimen> + <dimen name="wear_compose_material3_shape_corner_medium_size">18dp</dimen> + <dimen name="wear_compose_material3_shape_corner_large_size">26dp</dimen> + <dimen name="wear_compose_material3_shape_corner_extra_large_size">36dp</dimen> + </resources> diff --git a/PermissionController/res/values-zh-rCN-v35/strings.xml b/PermissionController/res/values-zh-rCN-v35/strings.xml new file mode 100644 index 000000000..c9da7729f --- /dev/null +++ b/PermissionController/res/values-zh-rCN-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"安全"</string> +</resources> diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml index 57634575e..031f48640 100644 --- a/PermissionController/res/values-zh-rCN/strings.xml +++ b/PermissionController/res/values-zh-rCN/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"权限"</string> <string name="cancel" msgid="8943320028373963831">"取消"</string> <string name="back" msgid="6249950659061523680">"返回"</string> + <string name="dialog_close" msgid="6840699812532384661">"关闭"</string> <string name="available" msgid="6007778121920339498">"可用"</string> <string name="blocked" msgid="9195547604866033708">"已禁用"</string> <string name="on" msgid="280241003226755921">"已开启"</string> @@ -202,14 +203,14 @@ <string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"查看“<xliff:g id="APP">%1$s</xliff:g>”的所有权限"</string> <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"查看具有此权限的所有应用"</string> <string name="assistant_mic_label" msgid="1011432357152323896">"显示 Google 助理麦克风使用情况"</string> - <string name="unused_apps_category_title" msgid="2988455616845243901">"针对闲置应用的设置"</string> + <string name="unused_apps_category_title" msgid="2988455616845243901">"闲置应用设置"</string> <string name="auto_revoke_label" msgid="5068393642936571656">"如果未使用此应用,则移除相关权限"</string> <string name="unused_apps_label" msgid="2595428768404901064">"撤消权限并释放空间"</string> <string name="unused_apps_label_v2" msgid="7058776770056517980">"暂停闲置应用的活动"</string> <string name="unused_apps_label_v3" msgid="693340578642156657">"管理闲置应用"</string> - <string name="unused_apps_summary" msgid="8839466950318403115">"移除权限、删除临时文件并停止发送通知"</string> - <string name="unused_apps_summary_v2" msgid="5011313200815115802">"撤消权限、删除临时文件、停止发送通知并归档应用"</string> - <string name="auto_revoke_summary" msgid="5867548789805911683">"为了保护您的数据,如果您连续几个月未使用此应用,系统会移除它的权限。"</string> + <string name="unused_apps_summary" msgid="8839466950318403115">"撤消权限、删除临时文件并停收通知"</string> + <string name="unused_apps_summary_v2" msgid="5011313200815115802">"撤消权限、删除临时文件、停收通知并归档应用"</string> + <string name="auto_revoke_summary" msgid="5867548789805911683">"为了保护您的数据,如果您连续几个月未使用此应用,系统会移除其权限。"</string> <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"为了保护您的数据,如果您连续几个月未使用此应用,系统会移除其以下权限:<xliff:g id="PERMS">%1$s</xliff:g>"</string> <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"为了保护您的数据,对于您连续几个月未使用过的应用,系统已将其权限移除。"</string> <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"如果您想重新授予权限,请打开应用。"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"无"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(系统默认)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"没有应用"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"其他 NFC 服务"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"已选择"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"已选择 - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"特殊应用权限"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g>请求上传调试信息。"</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"要分享调试数据吗?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"系统已检测到一个问题。"</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"“<xliff:g id="APP_NAME_0">%1$s</xliff:g>”请求上传此设备在<xliff:g id="DATE">%2$s</xliff:g><xliff:g id="TIME">%3$s</xliff:g>获取的错误报告。错误报告包含与您的设备有关或由应用记录的个人信息,例如:用户名、位置数据、设备标识符和网络信息。请务必只与您认为可以向其透露这些信息的人和应用分享错误报告。是否允许“<xliff:g id="APP_NAME_1">%4$s</xliff:g>”上传错误报告?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> 请求上传此设备在 <xliff:g id="DATE">%2$s</xliff:g><xliff:g id="TIME">%3$s</xliff:g> 获取的错误报告。错误报告包含与您的设备相关或者由应用记录的个人信息,例如用户名、位置数据、设备标识符和网络信息。请务必只与您信任的用户和应用分享错误报告。要允许 <xliff:g id="APP_NAME_1">%4$s</xliff:g> 上传错误报告吗?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"处理<xliff:g id="APP_NAME">%1$s</xliff:g>的错误报告时出错,因此系统已拒绝分享详细的调试数据。不便之处,敬请见谅。"</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"允许"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"拒绝"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>访问过"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>访问过"</string> <string name="test_otp_msg" msgid="7559110574222727550">"您的动态密码为 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"允许受限制的设置"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的设置"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"出于安全考虑,此设置目前不可用。"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"通话期间无法执行此类操作"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"在通话期间,不允许应用安装其他应用。\n\n诈骗者经常会在通话过程中要求执行此类操作,所以系统阻止了此类操作来保障您的安全。如果您正在陌生人的指引下执行此类操作,要当心,这可能是骗局。"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系统已拒绝向此应用授予<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>访问权限"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"该应用请求获得敏感权限,授予这项权限可能会导致您的个人信息和财务信息面临风险。<xliff:g id="ID_1"><br><br></xliff:g>如果不授予这项受限权限,该应用可能无法正常运行。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>了解如何授予访问权限</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系统已拒绝向此应用授予作为默认<xliff:g id="ROLE_NAME">%1$s</xliff:g>的访问权限"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"确定"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"权限请求被阻止"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"此应用请求获得额外的权限,但在流式传输会话期间无法授予权限。请先在手机上授予相应权限。"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"用于紧急呼叫或发送紧急短信"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"已向应急服务发送位置信息"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"在向紧急电话号码致电或发短信期间,此应用会获取您设备的位置信息。即使此应用没有位置信息权限或设备位置信息服务已关闭,也可能发生此情况。"<a href="https://support.google.com/android/answer/9319337">"了解详情"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"请查看您的手机"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"无法连接到您的手机"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"要重试吗?"</string> </resources> diff --git a/PermissionController/res/values-zh-rHK-v35/strings.xml b/PermissionController/res/values-zh-rHK-v35/strings.xml new file mode 100644 index 000000000..15c4d9bff --- /dev/null +++ b/PermissionController/res/values-zh-rHK-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"安全性"</string> +</resources> diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml index c3f40d3ff..64ced6518 100644 --- a/PermissionController/res/values-zh-rHK/strings.xml +++ b/PermissionController/res/values-zh-rHK/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"權限"</string> <string name="cancel" msgid="8943320028373963831">"取消"</string> <string name="back" msgid="6249950659061523680">"返回"</string> + <string name="dialog_close" msgid="6840699812532384661">"關閉"</string> <string name="available" msgid="6007778121920339498">"可用"</string> <string name="blocked" msgid="9195547604866033708">"已封鎖"</string> <string name="on" msgid="280241003226755921">"開啟"</string> @@ -361,14 +362,14 @@ <string name="role_browser_request_title" msgid="2895200507835937192">"要將 <xliff:g id="APP_NAME">%1$s</xliff:g> 設為預設瀏覽器應用程式嗎?"</string> <string name="role_browser_request_description" msgid="5888803407905985941">"無需任何權限"</string> <string name="role_dialer_label" msgid="1100224146343237968">"預設電話應用程式"</string> - <string name="role_dialer_short_label" msgid="7186888549465352489">"手機應用程式"</string> + <string name="role_dialer_short_label" msgid="7186888549465352489">"電話應用程式"</string> <string name="role_dialer_description" msgid="8768708633696539612">"此類應用程式允許你使用自己的裝置撥打和接聽電話"</string> <string name="role_dialer_request_title" msgid="5959618560705912058">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設手機應用程式嗎?"</string> <string name="role_dialer_request_description" msgid="6288839625724909320">"此應用程式將可存取你的相機、通訊錄、麥克風、電話及短訊"</string> <string name="role_dialer_search_keywords" msgid="3324448983559188087">"撥號器"</string> <string name="role_sms_label" msgid="8456999857547686640">"預設短訊應用程式"</string> <string name="role_sms_short_label" msgid="4371444488034692243">"短訊應用程式"</string> - <string name="role_sms_description" msgid="3424020199148153513">"此類應用程式允許你使用自己手機號碼傳送和接收短訊、相片、影片和其他資料"</string> + <string name="role_sms_description" msgid="3424020199148153513">"此類應用程式允許你使用自己的手機號碼傳送和接收短訊、相片、影片和其他資料"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設短訊應用程式嗎?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"此應用程式將可存取你的相機、通訊錄、檔案和媒體、麥克風、電話及短訊"</string> <string name="role_sms_search_keywords" msgid="8022048144395047352">"短訊, 發短訊, 訊息, 傳送短訊"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"無"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(系統預設)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"沒有應用程式"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"其他 NFC 服務"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"已選取"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"已選取 - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"特別應用程式存取權"</string> @@ -454,7 +456,7 @@ <string name="incident_report_notification_text" msgid="3376480583513587923">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」要求上載偵錯資料。"</string> <string name="incident_report_dialog_title" msgid="669104389325204095">"分享偵錯資料?"</string> <string name="incident_report_dialog_intro" msgid="5897733669850951832">"系統偵測到問題。"</string> - <string name="incident_report_dialog_text" msgid="5675553296891757523">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」要求上載此裝置於 <xliff:g id="DATE">%2$s</xliff:g><xliff:g id="TIME">%3$s</xliff:g>傳送的錯誤報告。錯誤報告包括你的裝置或應用程式記錄的個人資料,例如使用者名稱、位置資料、裝置識別碼和網絡資訊。只與你信任可存取這些資料的使用者和應用程式分享錯誤報告。要允許「<xliff:g id="APP_NAME_1">%4$s</xliff:g>」上載錯誤報告嗎?"</string> + <string name="incident_report_dialog_text" msgid="5675553296891757523">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」要求上載此裝置於 <xliff:g id="DATE">%2$s</xliff:g> <xliff:g id="TIME">%3$s</xliff:g> 傳送的錯誤報告。錯誤報告包括你的裝置或應用程式記錄的個人資料,例如使用者名稱、位置資料、裝置識別碼和網絡資訊。只與你信任可存取這些資料的使用者和應用程式分享錯誤報告。要允許「<xliff:g id="APP_NAME_1">%4$s</xliff:g>」上載錯誤報告嗎?"</string> <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"系統處理「<xliff:g id="APP_NAME">%1$s</xliff:g>」的錯誤報告時發生錯誤,因此已拒絕分享詳細的偵錯資料。很抱歉發生中斷情況。"</string> <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"允許"</string> <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"拒絕"</string> @@ -669,11 +671,14 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"上次存取時間:昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"上次存取時間:<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"你的一次性密碼是 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"允許受限設定"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限設定"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為安全起見,系統目前不提供此設定。"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"通話期間無法執行此操作"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話期間,應用程式不可安裝其他應用程式。\n\n由於騙徒經常在電話對話中要求這類操作,因此我們封鎖此功能以保障你的安全。如果有陌生人引導你執行此操作,那可能是詐騙。"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1"><br><br></xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>進一步瞭解如何授予存取權</a>"</string> - <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式預設<xliff:g id="ROLE_NAME">%1$s</xliff:g>存取權"</string> + <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式作為預設<xliff:g id="ROLE_NAME">%1$s</xliff:g>的存取權"</string> <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1"><br><br></xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>進一步瞭解如何授予存取權</a>"</string> <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"系統已拒絕授予應用程式存取權"</string> <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1"><br><br></xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>進一步瞭解如何授予存取權</a>"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"確定"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"權限要求被拒"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"此應用程式要求額外的權限,但串流工作階段期間無法授予權限。請先在手機上授予權限。"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"供緊急電話或短訊使用"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"已傳送位置給緊急服務"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"此應用程式在你與緊急電話號碼通話或收發短訊期間存取過你的裝置位置。即使應用程式沒有位置權限或裝置位置已關閉,也可能會發生這種情況。"<a href="https://support.google.com/android/answer/9319337">"瞭解詳情"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"請查看手機"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"無法連接你的手機"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"要重試嗎?"</string> </resources> diff --git a/PermissionController/res/values-zh-rTW-v35/strings.xml b/PermissionController/res/values-zh-rTW-v35/strings.xml new file mode 100644 index 000000000..15c4d9bff --- /dev/null +++ b/PermissionController/res/values-zh-rTW-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"安全性"</string> +</resources> diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml index 9bedb8fc1..e98024c52 100644 --- a/PermissionController/res/values-zh-rTW/strings.xml +++ b/PermissionController/res/values-zh-rTW/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"權限"</string> <string name="cancel" msgid="8943320028373963831">"取消"</string> <string name="back" msgid="6249950659061523680">"返回"</string> + <string name="dialog_close" msgid="6840699812532384661">"關閉"</string> <string name="available" msgid="6007778121920339498">"可用"</string> <string name="blocked" msgid="9195547604866033708">"已封鎖"</string> <string name="on" msgid="280241003226755921">"開啟"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"無"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(系統預設)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"沒有可用的應用程式"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"其他 NFC 服務"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"已選取"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"已選取 - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"特殊應用程式存取權"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"上次存取時間:昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"上次存取時間:<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"你的動態密碼為 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"解除受限制的設定"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的設定"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為了安全起見,目前無法使用這項設定。"</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"無法在通話期間執行這項操作"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"通話期間不允許應用程式安裝其他應用程式。\n\n詐騙者經常會在電話對話中要求這類操作,因此系統加以封鎖來保護你的安全。如果有陌生人引導你採取這類操作,請提高警覺,這可能是詐騙行為。"</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"這個應用程式要求取得私密資訊權限,授予這項權限可能導致你的個人資訊和財務資訊面臨風險。<xliff:g id="ID_1"><br><br></xliff:g>如果未取得這項受限制權限,應用程式可能無法正常運作。<a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>瞭解如何授予權限</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式做為預設「<xliff:g id="ROLE_NAME">%1$s</xliff:g>」的存取權"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"確定"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"權限要求遭拒"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"這個應用程式要求額外權限,但串流期間無法授權。請先在手機上授予權限。"</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"用於緊急電話或簡訊"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"位置資訊已傳送至緊急救援服務"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"這個應用程式已在你撥打緊急電話號碼或傳送簡訊期間,存取裝置的位置資訊。即使應用程式並未取得位置存取權,或是裝置定位功能已關閉,也可能發生這種情況。"<a href="https://support.google.com/android/answer/9319337">"瞭解詳情"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"查看手機"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"無法連上手機"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"要重試嗎?"</string> </resources> diff --git a/PermissionController/res/values-zu-v35/strings.xml b/PermissionController/res/values-zu-v35/strings.xml new file mode 100644 index 000000000..39381b3ce --- /dev/null +++ b/PermissionController/res/values-zu-v35/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="safety_center_entries_category_title" msgid="4413287601344137909">"Ukuvikeleka"</string> +</resources> diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml index bc97d7000..95f837307 100644 --- a/PermissionController/res/values-zu/strings.xml +++ b/PermissionController/res/values-zu/strings.xml @@ -21,6 +21,7 @@ <string name="permission_search_keyword" msgid="1214451577494730543">"izimvume"</string> <string name="cancel" msgid="8943320028373963831">"Khansela"</string> <string name="back" msgid="6249950659061523680">"Emuva"</string> + <string name="dialog_close" msgid="6840699812532384661">"Vala"</string> <string name="available" msgid="6007778121920339498">"Iyatholakala"</string> <string name="blocked" msgid="9195547604866033708">"Uvinjiwe"</string> <string name="on" msgid="280241003226755921">"Vuliwe"</string> @@ -440,6 +441,7 @@ <string name="default_app_none" msgid="9084592086808194457">"Lutho"</string> <string name="default_app_system_default" msgid="6218386768175513760">"(Okuzenzakalelayo kwesistimu)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Azikho izinhlelo zokusebenza"</string> + <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Amanye amasevisi we-NFC"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Ekhethiwe"</string> <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Ekhethiwe - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string> <string name="special_app_access_search_keyword" msgid="8032347212290774210">"ukufinyelela kwi-app okukhethekile"</string> @@ -669,8 +671,11 @@ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kufinyelelwe izolo ngo-<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string> <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kufinyelelwe ngo-<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string> <string name="test_otp_msg" msgid="7559110574222727550">"Iphasiwedi yakho yesikhathi esisodwa ithi 132435"</string> + <string name="allow_restricted_settings" msgid="8073000189478396881">"Vumela amasethingi akhawulelwe"</string> <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Amasethingi akhawulelwe"</string> <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ukuze uphephe, leli sethingi okwamanje alitholakali."</string> + <string name="enhanced_confirmation_phone_state_dialog_title" msgid="5230100829862738467">"Isenzo asitholakali ngenkathi ufona"</string> + <string name="enhanced_confirmation_phone_state_dialog_desc" msgid="8782160971908273849">"Ukuvumela ama-app ukuthi afake amanye ama-app akuvunyelwe phakathi nekholi yefoni.\n\n Amaqola, ngokuvamile acela lolu hlobo lwesenzo phakathi nezingxoxo zekholi, ngakho-ke kuvinjiwe ukuze uvikeleke. Uma uqondiswa ukuthi wenze lesi senzo ngumuntu ongamazi, kungase kube umkhonyovu."</string> <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"I-app inqatshelwe ukufinyelela ku-<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string> <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"I-app icele ukufinyelela emvumweni ezwelayo okungabeka imininingwane yakho siqu neyezimali engozini.<xliff:g id="ID_1"><br><br></xliff:g>Kungenzeka ukuthi i-app ingasebenzi kahle ngaphandle kwemvume enomkhawulo. <a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>>Funda ukuthi ungakuvumela kanjani ukufinyelela</a>"</string> <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"I-app iye yanqatshelwa ukufinyelela ukuze ibe yi-<xliff:g id="ROLE_NAME">%1$s</xliff:g> ezenzakalelayo"</string> @@ -681,4 +686,10 @@ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"KULUNGILE"</string> <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Isicelo semvume sicindezelwe"</string> <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Le-app icela izimvume ezengeziwe, kodwa izimvume azikwazi ukunikezwa ngesikhathi sokusakaza-bukhoma. Nikeza imvume kufoni yakho kuqala."</string> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Okwekholi yezimo eziphuthumayo noma umyalezo"</string> + <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Indawo ithunyelwe emasevisini ezimo eziphuthumayo"</string> + <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Le app ifinyelele indawo yedivayisi yakho phakathi nekholi noma umyalezo othunyelwe enombolweni yezimo eziphuthumayo. Lokhu kungenzeka lapho i-app ingenayo imvume yendawo noma uma indawo yedivayisi ivaliwe. "<a href="https://support.google.com/android/answer/9319337">"Funda kabanzi"</a></string> + <string name="wear_check_your_phone_title" msgid="7957291983474981420">"Hlola ifoni yakho"</string> + <string name="wear_phone_connection_error" msgid="8934350845737930783">"Ayikwazanga ukufinyelela efonini yakho"</string> + <string name="wear_phone_connection_should_retry" msgid="2835355845567088003">"Zama futhi?"</string> </resources> diff --git a/PermissionController/res/values/attrs.xml b/PermissionController/res/values/attrs.xml index cc93effde..0634ab5f1 100644 --- a/PermissionController/res/values/attrs.xml +++ b/PermissionController/res/values/attrs.xml @@ -37,4 +37,9 @@ <attr name="carDividerColor" format="color"/> <!-- END: Car Settings Preferences --> + <!-- START: AppPermissionFragment --> + <attr name="extraWidgetId" format="reference" /> + <attr name="extraWidgetIcon" format="reference" /> + <attr name="checkboxId" format="reference" /> + <!-- END: AppPermissionFragment --> </resources> diff --git a/PermissionController/res/values/bools.xml b/PermissionController/res/values/bools.xml index b5f33b081..4483fc48f 100644 --- a/PermissionController/res/values/bools.xml +++ b/PermissionController/res/values/bools.xml @@ -20,4 +20,6 @@ <bool name="is_at_least_t">false</bool> <bool name="is_at_least_u">false</bool> <bool name="is_at_least_v">false</bool> + <bool name="config_usePreferenceForAppPermissionSettings">false</bool> + <bool name="config_appPermissionFooterLinkPreferenceSummaryUnderlined">false</bool> </resources> diff --git a/PermissionController/res/values/config.xml b/PermissionController/res/values/config.xml index 8e3a58abd..675368cba 100644 --- a/PermissionController/res/values/config.xml +++ b/PermissionController/res/values/config.xml @@ -27,4 +27,6 @@ <!-- Whether to show the "Show password" toggle --> <bool name="config_display_show_password_toggle">true</bool> + + <bool name="config_permissionFooterPreferenceIconVisible">true</bool> </resources> diff --git a/PermissionController/res/values/dimens.xml b/PermissionController/res/values/dimens.xml index 53d5341fe..f838fea45 100644 --- a/PermissionController/res/values/dimens.xml +++ b/PermissionController/res/values/dimens.xml @@ -23,9 +23,12 @@ <dimen name="header_margin_start">16dp</dimen> <dimen name="header_margin_end">16dp</dimen> - <dimen name="permission_icon_size">24dp</dimen> + <dimen name="tv_permission_icon_size">24dp</dimen> <dimen name="permission_icon_header_size">32dp</dimen> + <dimen name="permission_preference_app_icon_size">@dimen/secondary_app_icon_size</dimen> + <dimen name="permission_preference_permission_group_icon_size">24dp</dimen> + <dimen name="lb_action_section_width">384dp</dimen> <dimen name="lb_content_section_width">576dp</dimen> @@ -70,6 +73,31 @@ <dimen name="location_accuracy_image_size">130dp</dimen> + <dimen name="toggle_preference_container_min_width">56dp</dimen> + + <dimen name="car_location_permission_prompt_radius">40dp</dimen> + <dimen name="car_location_permission_prompt_icon_size">32dp</dimen> + <dimen name="car_location_permission_prompt_padding_large">40dp</dimen> + <dimen name="car_location_permission_prompt_padding">24dp</dimen> + <dimen name="car_location_permission_prompt_padding_medium">20dp</dimen> + <dimen name="car_location_permission_prompt_padding_small">16dp</dimen> + <dimen name="car_location_permission_prompt_padding_extra_small">6dp</dimen> + <dimen name="car_location_permission_prompt_margin_large">32dp</dimen> + <dimen name="car_location_permission_prompt_margin_medium">24dp</dimen> + <dimen name="car_location_permission_prompt_margin_small">12dp</dimen> + <dimen name="car_location_permission_prompt_margin_xsmall">8dp</dimen> + <dimen name="car_location_permission_prompt_margin_extra_small">6dp</dimen> + <dimen name="car_location_permission_prompt_min_width">520dp</dimen> + <dimen name="car_location_permission_prompt_min_height">64dp</dimen> + <dimen name="car_location_permission_prompt_button_text_size">28sp</dimen> + <dimen name="car_location_permission_prompt_text_size_xlarge">32sp</dimen> + <dimen name="car_location_permission_prompt_line_height_xlarge">40sp</dimen> + <dimen name="car_location_permission_prompt_text_size_large">24sp</dimen> + <dimen name="car_location_permission_prompt_text_size_medium">20sp</dimen> + <dimen name="car_location_permission_prompt_text_size_small">16sp</dimen> + <dimen name="car_location_permission_prompt_line_height_small">36sp</dimen> + <dimen name="car_location_permission_prompt_radio_button_text_size">28sp</dimen> + <item name="permission_access_time_dialog_width_scalar" format="float" type="dimen">0.9</item> <item name="permission_access_time_dialog_height_scalar" format="float" type="dimen">0.7</item> diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml index 0cf52ac4a..ea7929746 100644 --- a/PermissionController/res/values/overlayable.xml +++ b/PermissionController/res/values/overlayable.xml @@ -27,6 +27,73 @@ <item type="string" name="help_url_action_disabled_by_restricted_settings" /> <!-- END HELP LINKS --> + <!-- START PREFERENCE STYLES. Used in V35 only --> + <item type="style" name="PermissionPreferenceCategoryRootLayoutStyle" /> + <item type="style" name="PermissionPreferenceCategoryTextRelativeLayoutStyle" /> + <item type="style" name="PermissionPreferenceCategoryTitleTextStyle" /> + <item type="style" name="PermissionPreferenceCategorySummaryTextStyle" /> + + <item type="style" name="PermissionPreferenceRootLayoutStyle" /> + <item type="style" name="PermissionPreferenceIconFrameStyle" /> + <item type="style" name="PermissionPreferenceIconStyle" /> + <item type="style" name="PermissionPreferenceTextRelativeLayoutStyle" /> + <item type="style" name="PermissionPreferenceTitleTextStyle" /> + <item type="style" name="PermissionPreferenceSummaryTextStyle" /> + <item type="style" name="PermissionPreferenceWidgetFrameLayoutStyle" /> + + <item type="style" name="PermissionFooterPreferenceRootLayoutStyle" /> + <item type="style" name="PermissionFooterPreferenceIconFrameStyle" /> + <item type="style" name="PermissionFooterPreferenceIconStyle" /> + <item type="style" name="PermissionFooterPreferenceLearnMoreTextLayoutStyle" /> + <item type="style" name="PermissionFooterPreferenceTitleTextStyle" /> + <item type="style" name="PermissionFooterPreferenceLearnMoreTextStyle" /> + + <item type="style" name="PermissionSelectorWithWidgetPreferenceRootLayoutStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceWidgetFrameStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceIconFrameStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceIconStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceTextContainerStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceTitleStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceSummaryContainerStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceSummaryStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceAppendixStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetContainerStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetDividerStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceExtraWidgetImageStyle" /> + <item type="style" name="PermissionSelectorWithWidgetPreferenceWidgetRadioButton" /> + + <item type="style" name="PermissionTwoTargetPreferenceRootLayoutStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceTextContainerStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceTitleStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceSummaryStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceWidgetFrameStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceIconFrameStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceIconStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceDividerContainerStyle" /> + <item type="style" name="PermissionTwoTargetPreferenceDividerStyle" /> + + <item type="style" name="AppPermissionFooterLinkPreferenceRootLayoutStyle" /> + <item type="style" name="AppPermissionFooterLinkPreferenceTextLayoutStyle" /> + <item type="style" name="AppPermissionFooterLinkPreferenceSummaryStyle" /> + + <item type="bool" name="config_usePreferenceForAppPermissionSettings" /> + <item type="bool" name="config_permissionFooterPreferenceIconVisible" /> + <item type="bool" name="config_appPermissionFooterLinkPreferenceSummaryUnderlined" /> + <item type="dimen" name="permission_preference_app_icon_size" /> + <item type="dimen" name="permission_preference_permission_group_icon_size" /> + + <!-- END PREFERENCE STYLES --> + + <!-- START AUTO PERMISSION GRANT DIALOG --> + + <item type="style" name="AutoPermissionLocationAccuracyRadioFine" /> + <item type="style" name="AutoPermissionLocationAccuracyRadioCoarse" /> + <item type="style" name="AutoPermissionGrantDialogMaterial3" /> + <item type="style" name="AutoPermissionGrantTitleMessage" /> + <item type="style" name="AutoPermissionGrantButton" /> + + <!-- END AUTO PERMISSION GRANT DIALOG --> + <!-- START PERMISSION GRANT DIALOG --> <item type="style" name="PermissionGrantScrollView" /> <item type="style" name="PermissionGrantSingleton" /> @@ -287,6 +354,10 @@ <item type="dimen" name="secondary_app_icon_size" /> <!-- END SETTINGSLIB APPPREFERENCE --> + <!-- START ATTRS --> + <item type="attr" name="state_has_icon_space" /> + <!-- END ATTRS --> + <!-- START THEMES --> <item type="style" name="Settings" /> <item type="style" name="TextAppearance.CategoryTitle" /> @@ -296,6 +367,8 @@ <item type="style" name="RequestRole" /> <item type="style" name="Theme.PermissionGrantDialog" /> <item type="style" name="PermissionDialog" /> + <!-- Theme overlay applied to permission settings UI on v35+ --> + <item type="style" name="ThemeOverlay.PermissionSettings" /> <!-- END THEMES --> <!-- START VISIBILITY CONFIGS --> @@ -328,6 +401,7 @@ <!-- END CAR THEMES --> <!-- START LAYOUTPREFERENCE --> + <!-- {@deprecated Use SettingsLibEntityHeaderTitle.} --> <item type="style" name="TextAppearance.EntityHeaderTitle" /> <!-- END LAYOUTPREFERENCE --> @@ -387,7 +461,7 @@ <item type="style" name="AppDataSharingUpdateSettingsIcon" /> <!-- END SAFETY LABELS STYLE --> - <!--START WEAR SPECIFIC FONT STRINGS --> + <!--START WEAR SPECIFIC MATERIAL2 FONT STRINGS --> <item type="string" name="wear_material_compose_display_1_font_family" /> <item type="string" name="wear_material_compose_display_2_font_family" /> <item type="string" name="wear_material_compose_display_3_font_family" /> @@ -400,7 +474,75 @@ <item type="string" name="wear_material_compose_caption_1_font_family" /> <item type="string" name="wear_material_compose_caption_2_font_family" /> <item type="string" name="wear_material_compose_caption_3_font_family" /> - <!--END WEAR SPECIFIC FONT STRINGS --> + <!--END WEAR SPECIFIC MATERIAL2 FONT STRINGS --> + + <!--START WEAR SPECIFIC MATERIAL3 FONT FACE TOKENS--> + <item type="string" name="wear_compose_material3_arc_small_font_family" /> + <item type="string" name="wear_compose_material3_arc_medium_font_family" /> + <item type="string" name="wear_compose_material3_arc_large_font_family" /> + + <item type="string" name="wear_compose_material3_body_extra_small_font_family" /> + <item type="string" name="wear_compose_material3_body_small_font_family" /> + <item type="string" name="wear_compose_material3_body_medium_font_family" /> + <item type="string" name="wear_compose_material3_body_large_font_family" /> + + <item type="string" name="wear_compose_material3_display_small_font_family" /> + <item type="string" name="wear_compose_material3_display_medium_font_family" /> + <item type="string" name="wear_compose_material3_display_large_font_family" /> + + <item type="string" name="wear_compose_material3_label_small_font_family" /> + <item type="string" name="wear_compose_material3_label_medium_font_family" /> + <item type="string" name="wear_compose_material3_label_large_font_family" /> + + <item type="string" name="wear_compose_material3_numeral_extra_small_font_family" /> + <item type="string" name="wear_compose_material3_numeral_small_font_family" /> + <item type="string" name="wear_compose_material3_numeral_medium_font_family" /> + <item type="string" name="wear_compose_material3_numeral_large_font_family" /> + <item type="string" name="wear_compose_material3_numeral_extra_large_font_family" /> + + <item type="string" name="wear_compose_material3_title_small_font_family" /> + <item type="string" name="wear_compose_material3_title_medium_font_family" /> + <item type="string" name="wear_compose_material3_title_large_font_family" /> + <!--END WEAR SPECIFIC MATERIAL3 FONT FACE TOKENS--> + + + <!--START WEAR SPECIFIC MATERIAL3 FONT SIZE TOKENS--> + <item type="dimen" name="wear_compose_material3_arc_small_font_size" /> + <item type="dimen" name="wear_compose_material3_arc_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_arc_large_font_size" /> + + <item type="dimen" name="wear_compose_material3_body_extra_small_font_size" /> + <item type="dimen" name="wear_compose_material3_body_small_font_size" /> + <item type="dimen" name="wear_compose_material3_body_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_body_large_font_size" /> + + <item type="dimen" name="wear_compose_material3_display_small_font_size" /> + <item type="dimen" name="wear_compose_material3_display_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_display_large_font_size" /> + + <item type="dimen" name="wear_compose_material3_label_small_font_size" /> + <item type="dimen" name="wear_compose_material3_label_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_label_large_font_size" /> + + <item type="dimen" name="wear_compose_material3_numeral_extra_small_font_size" /> + <item type="dimen" name="wear_compose_material3_numeral_small_font_size" /> + <item type="dimen" name="wear_compose_material3_numeral_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_numeral_large_font_size" /> + <item type="dimen" name="wear_compose_material3_numeral_extra_large_font_size" /> + + <item type="dimen" name="wear_compose_material3_title_small_font_size" /> + <item type="dimen" name="wear_compose_material3_title_medium_font_size" /> + <item type="dimen" name="wear_compose_material3_title_large_font_size" /> + <!--END WEAR SPECIFIC MATERIAL3 FONT SIZE TOKENS--> + + <!--START WEAR SPECIFIC MATERIAL3 SHAPE TOKENS--> + <item type="dimen" name="wear_compose_material3_shape_corner_extra_small_size" /> + <item type="dimen" name="wear_compose_material3_shape_corner_small_size" /> + <item type="dimen" name="wear_compose_material3_shape_corner_medium_size" /> + <item type="dimen" name="wear_compose_material3_shape_corner_large_size" /> + <item type="dimen" name="wear_compose_material3_shape_corner_extra_large_size" /> + <!--END WEAR SPECIFIC MATERIAL3 SHAPE TOKENS--> + <!-- START ENHANCED CONFIRMATION DIALOG --> <item type="style" name="Theme.EnhancedConfirmationDialog" /> @@ -416,6 +558,15 @@ <item type="style" name="EnhancedConfirmationDialogButton" /> <!-- END ENHANCED CONFIRMATION DIALOG --> + <!-- START STETINGS LIB HEADER --> + <item type="style" name="SettingsLibEntityHeader" /> + <item type="style" name="SettingsLibEntityHeaderContent" /> + <item type="style" name="SettingsLibEntityHeaderIcon" /> + <item type="style" name="SettingsLibEntityHeaderTitle" /> + + <item type="bool" name="config_useCollapsingToolbarInPermissionSettings"/> + <!-- END SETTINGS LIB HEADER --> + </policy> </overlayable> diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml index 07c919f1f..82c7889c6 100644 --- a/PermissionController/res/values/strings.xml +++ b/PermissionController/res/values/strings.xml @@ -30,6 +30,9 @@ <!-- Button label for back action [CHAR LIMIT=20] --> <string name="back">Back</string> + <!-- Button label for closing a dialog action --> + <string name="dialog_close">Close</string> + <!-- Label indicating access is allowed. If you update this string, please update the string "quick_settings_camera_mic_available" in System Ui [CHAR LIMIT=NONE] --> <string name="available">Available</string> @@ -1240,6 +1243,17 @@ <string name="role_wallet_request_title">Set <xliff:g id="app_name" example="Super Wallet">%1$s</xliff:g> as your default wallet app?</string> <string name="role_wallet_request_description">No permissions needed</string> + <!-- Label for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] --> + <string name="role_for_testing_profile_group_exclusivity_label" translatable="false">Default test profile group exclusive role app</string> + <!-- Short label for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] --> + <string name="role_for_testing_profile_group_exclusivity_short_label" translatable="false">Test profile group exclusive role app</string> + <!-- Description for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] --> + <string name="role_for_testing_profile_group_exclusivity_description" translatable="false">Test profile group exclusive role apps are for tests only and should not be held by production apps.</string> + <!-- Request title for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] --> + <string name="role_for_testing_profile_group_exclusivity_request_title" translatable="false">Set <xliff:g id="app_name" example="Super test app">%1$s</xliff:g> as your default test profile group exclusive role app?</string> + <!-- Request description for the RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY role. [DO NOT TRANSLATE] --> + <string name="role_for_testing_profile_group_exclusivity_request_description" translatable="false">No permissions needed</string> + <!-- Subtitle for the application that is the current default application [CHAR LIMIT=30] --> <string name="request_role_current_default">Current default</string> @@ -1326,6 +1340,9 @@ <!-- Label when there are no apps available for a default app [CHAR LIMIT=30] --> <string name="default_app_no_apps">No apps</string> + <!-- Title for preference item that will open a screen to manage other NFC services [CHAR LIMIT=50] --> + <string name="default_payment_app_other_nfc_services">Other NFC services</string> + <!-- Label for the selected default app for default app [CHAR LIMIT=30] --> <string name="car_default_app_selected">Selected</string> @@ -1989,6 +2006,9 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!-- A string representing a message (sms, email, etc.) telling the user about a one time password. Used for testing [CHAR LIMIT=NONE] --> <string name="test_otp_msg">Your one time password is 132435</string> + <!-- [CHAR LIMIT=50] Manage applications, unlock restricted setting from app permissions options menu --> + <string name="allow_restricted_settings">Allow restricted settings</string> + <!-- START ENHANCED CONFIRMATION DIALOG --> <!--Title for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=50] --> @@ -1996,6 +2016,13 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] --> <string name="enhanced_confirmation_dialog_desc">For your security, this setting is currently unavailable.</string> + <!--Title for dialog displayed to tell user that settings are blocked due to the phone state (such as being in a call with an unknown number) [CHAR LIMIT=50] --> + <string name="enhanced_confirmation_phone_state_dialog_title">Action not available while on a phone call</string> + <!--Content for dialog displayed to tell user that settings are blocked due to the phone state (such as being in a call with an unknown number) [CHAR LIMIT=NONE] --> + <string name="enhanced_confirmation_phone_state_dialog_desc">Allowing apps to install other apps is not allowed during a phone call.\n\n + Scammers often request this type of action during phone call conversations, so it\u2019s blocked to protect you. If you are being guided to take this action + by someone you don\u2019t know, it might be a scam.</string> + <!--Title for dialog displayed to tell user that permissions are blocked by setting restrictions [CHAR LIMIT=50] --> <string name="enhanced_confirmation_dialog_title_permission">App was denied access to <xliff:g id="permission_name" example="contacts">%1$s</xliff:g></string> <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] --> @@ -2024,6 +2051,14 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!-- Descriptions for the warning dialog that shows when permission grant dialog is blocked from streaming to a remote device [CHAR LIMIT=200] --> <string name="permission_grant_dialog_streaming_blocked_description">This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first.</string> + <!-- Attribution label that is enforced to show for emergency location accesses in the privacy dashboard --> + <string name="privacy_dashboard_emergency_location_enforced_attribution_label">For emergency call or text</string> + <!-- Title of the dialog when clicking the app op access entry for EMERGENCY_LOCATION in the privacy dashboard --> + <string name="privacy_dashboard_emergency_location_dialog_title">Location sent to emergency services</string> + <!-- Description of the dialog when clicking the app op access entry for EMERGENCY_LOCATION in the privacy dashboard --> + <string name="privacy_dashboard_emergency_location_dialog_description">This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. <a href="https://support.google.com/android/answer/9319337">Learn more</a></string> + <!-- Close button in the pop up dialog when clicking the app op access entry in the privacy dashboard --> + <!-- Text instructing user to check their paired phone--> <string name="wear_check_your_phone_title">Check your phone</string> diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml index 043934ac4..c764e776e 100644 --- a/PermissionController/res/values/styles.xml +++ b/PermissionController/res/values/styles.xml @@ -17,6 +17,108 @@ <resources> + <!-- START AUTO PERMISSION GRANT DIALOG --> + + <style name="AutoPermissionGrantScrollView"> + <item name="android:scrollbars">none</item> + <item name="android:fillViewport">true</item> + <item name="android:clipChildren">false</item> + </style> + + <style name="AutoPermissionGrantSingleton"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center</item> + </style> + + <style name="AutoPermissionGrantDialogMaterial3"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + <item name="android:padding">@dimen/car_location_permission_prompt_padding_large</item> + <item name="android:background">@drawable/auto_grant_permissions_dialog</item> + </style> + + <style name="AutoPermissionGrantContent"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + <item name="android:paddingBottom">@dimen/car_location_permission_prompt_padding_medium</item> + </style> + + <style name="AutoPermissionGrantDescription"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + <item name="android:gravity">start</item> + </style> + + <style name="AutoPermissionGrantTitleMessage" parent="@android:style/TextAppearance.DeviceDefault.WindowTitle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:maxWidth">@dimen/car_location_permission_prompt_min_width</item> + <item name="android:textSize">@dimen/car_location_permission_prompt_text_size_xlarge</item> + <item name="android:lineHeight">@dimen/car_location_permission_prompt_line_height_xlarge</item> + <item name="android:layout_marginBottom">@dimen/car_location_permission_prompt_margin_small</item> + <item name="android:textAppearance">?android:attr/textAppearanceLarge</item> + </style> + + <style name="AutoPermissionGrantDetailMessage" parent="@android:style/TextAppearance.DeviceDefault"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginTop">@dimen/car_location_permission_prompt_margin_small</item> + <item name="android:textSize">@dimen/car_location_permission_prompt_text_size_small</item> + </style> + + <style name="AutoPermissionLocationAccuracyRadioGroupMaterial3"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginBottom">@dimen/car_location_permission_prompt_margin_medium</item> + <item name="android:gravity">center_horizontal</item> + <item name="android:orientation">horizontal</item> + </style> + + <style name="AutoPermissionLocationAccuracyRadioFine"> + <item name="android:button">@null</item> + <item name="android:background">@null</item> + <item name="android:gravity">center_horizontal</item> + <item name="android:layout_marginEnd">@dimen/car_location_permission_prompt_margin_large</item> + <item name="android:drawablePadding">8dp</item> + <item name="android:textSize">@dimen/car_location_permission_prompt_radio_button_text_size</item> + <item name="android:lineHeight">@dimen/car_location_permission_prompt_line_height_small</item> + </style> + + <style name="AutoPermissionLocationAccuracyRadioCoarse"> + <item name="android:button">@null</item> + <item name="android:background">@null</item> + <item name="android:gravity">center_horizontal</item> + <item name="android:layout_marginStart">@dimen/car_location_permission_prompt_margin_large</item> + <item name="android:drawablePadding">8dp</item> + <item name="android:textSize">@dimen/car_location_permission_prompt_radio_button_text_size</item> + <item name="android:lineHeight">@dimen/car_location_permission_prompt_line_height_small</item> + </style> + + <style name="AutoPermissionLocationAccuracyImageViewMaterial13"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginBottom">@dimen/car_location_permission_prompt_margin_medium</item> + </style> + + <style name="AutoPermissionGrantButtonListMaterial3"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + </style> + + <style name="AutoPermissionGrantButtonBarSpace"> + <item name="android:layout_width">0dp</item> + <item name="android:layout_height">0dp</item> + <item name="android:visibility">gone</item> + </style> + + <!-- END AUTO PERMISSION GRANT DIALOG --> + + <!-- START PERMISSION GRANT DIALOG --> <style name="PermissionGrantScrollView"> @@ -1370,6 +1472,7 @@ <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginTop">16dp</item> <item name="android:gravity">center_horizontal</item> + <item name="android:fontFamily">google-sans</item> </style> <style name="EnhancedConfirmationDialogScrollView"> diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml index 2e6af4af1..9aa14a64c 100644 --- a/PermissionController/res/values/themes.xml +++ b/PermissionController/res/values/themes.xml @@ -107,6 +107,8 @@ <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" /> + <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" /> + <!-- TODO(b/309578419): Make activities handle insets properly and then remove this. --> @@ -152,7 +154,7 @@ <!-- START ENHANCED CONFIRMATION DIALOG --> - <style name="Theme.EnhancedConfirmationDialog" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> + <style name="Theme.EnhancedConfirmationDialog" parent="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight"> <item name="windowNoTitle">true</item> <item name="android:alertDialogTheme">@style/Theme.EnhancedConfirmationDialogFragment</item> <item name="android:buttonBarStyle">@style/EnhancedConfirmationDialogButtonBar</item> @@ -161,7 +163,7 @@ <item name="android:buttonBarNeutralButtonStyle">@style/EnhancedConfirmationDialogButton</item> </style> - <style name="Theme.EnhancedConfirmationDialogFragment" parent="@android:style/Theme.DeviceDefault.Dialog.Alert"> + <style name="Theme.EnhancedConfirmationDialogFragment" parent="@style/Theme.DeviceDefault.Dialog.Alert.DayNight"> <item name="android:buttonBarStyle">@style/EnhancedConfirmationDialogButtonBar</item> </style> diff --git a/PermissionController/res/xml-v35/app_permission.xml b/PermissionController/res/xml-v35/app_permission.xml new file mode 100644 index 000000000..87315815d --- /dev/null +++ b/PermissionController/res/xml-v35/app_permission.xml @@ -0,0 +1,102 @@ +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory + android:key="app_permission_rationale_container" + android:title="@string/app_permission_rationale_message" + app:isPreferenceVisible="false"> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreference + android:key="app_permission_rationale" + android:icon="@drawable/ic_shield_exclamation_outline_24dp" + android:summary="@string/app_location_permission_rationale_subtitle" + android:title="@string/app_location_permission_rationale_title" /> + + </com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory + android:key="app_permission_button_category" + android:title="@string/app_permission_header"> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_allow_radio_button" + android:title="@string/app_permission_button_allow" + app:checkboxId="@+id/allow_radio_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_allow_always_radio_button" + android:title="@string/app_permission_button_allow_always" + app:checkboxId="@+id/allow_always_radio_button" + app:isPreferenceVisible="false" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_allow_foreground_only_radio_button" + android:title="@string/app_permission_button_allow_foreground" + app:checkboxId="@+id/allow_foreground_only_radio_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_select_photos_radio_button" + android:title="@string/app_permission_button_allow_limited_access" + app:checkboxId="@+id/select_radio_button" + app:extraWidgetIcon="@drawable/ic_edit" + app:extraWidgetId="@+id/edit_selected_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_ask_one_time_radio_button" + android:title="@string/app_permission_button_ask" + app:checkboxId="@+id/ask_one_time_radio_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_ask_radio_button" + android:text="@string/app_permission_button_ask" + android:title="@string/app_permission_button_ask" + app:checkboxId="@+id/ask_radio_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_deny_radio_button" + android:title="@string/app_permission_button_deny" + app:checkboxId="@+id/deny_radio_button" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionSelectorWithWidgetPreference + android:key="app_permission_deny_foreground_radio_button" + android:title="@string/app_permission_button_deny" + app:checkboxId="@+id/deny_foreground_radio_button" /> + + </com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionSwitchPreference + android:key="app_permission_location_accuracy_switch" + android:summary="@string/app_permission_location_accuracy_subtitle" + android:title="@string/app_permission_location_accuracy" + app:isPreferenceVisible="false" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.PermissionTwoTargetPreference + android:key="app_permission_details" + android:selectable="false" + android:summary="@string/permission_summary_enabled_system_fixed" + app:extraWidgetIcon="@drawable/ic_settings" + app:iconSpaceReserved="true" + app:isPreferenceVisible="false" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference + android:key="app_permission_footer_link_1" + android:summary="@string/app_permission_footer_app_permissions_link" /> + + <com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference + android:key="app_permission_footer_link_2" + android:summary="@string/app_permission_footer_permission_apps_link" /> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference + android:key="app_permission_footer_storage_special_app_access" + android:icon="@drawable/ic_info_outline" + android:title="@string/app_permission_footer_special_file_access" + app:isPreferenceVisible="false" /> + + <com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference + android:key="app_permission_additional_info" + android:icon="@drawable/ic_info_outline" + android:title="@string/exempt_info_label" + app:isPreferenceVisible="false" /> + +</PreferenceScreen> diff --git a/PermissionController/res/xml/all_permissions.xml b/PermissionController/res/xml/all_permissions.xml index 5f7f847ba..5e97f36af 100644 --- a/PermissionController/res/xml/all_permissions.xml +++ b/PermissionController/res/xml/all_permissions.xml @@ -17,7 +17,7 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/all_permissions"> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="other_perms" android:title="@string/other_permissions" /> diff --git a/PermissionController/res/xml/allowed_denied.xml b/PermissionController/res/xml/allowed_denied.xml index 0e6c27268..f283b2c5c 100644 --- a/PermissionController/res/xml/allowed_denied.xml +++ b/PermissionController/res/xml/allowed_denied.xml @@ -17,23 +17,23 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="allowed" android:title="@string/allowed_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="allowed_foreground" android:title="@string/allowed_foreground_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="ask" android:title="@string/ask_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="denied" android:title="@string/denied_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="storage_footer_category" /> </PreferenceScreen> diff --git a/PermissionController/res/xml/allowed_denied_storage.xml b/PermissionController/res/xml/allowed_denied_storage.xml index e6fdc5dea..3a61f92f3 100644 --- a/PermissionController/res/xml/allowed_denied_storage.xml +++ b/PermissionController/res/xml/allowed_denied_storage.xml @@ -18,23 +18,23 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="allowed_storage_full" android:title="@string/allowed_storage_full" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="allowed_storage_scoped" android:title="@string/allowed_storage_scoped" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="allowed_foreground" android:title="@string/allowed_foreground_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="ask" android:title="@string/ask_header" /> - <PreferenceCategory + <com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory android:key="denied" android:title="@string/denied_header" /> diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 6e65b96e3..3d858a9c5 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -92,12 +92,22 @@ <permission name="android.permission.POST_NOTIFICATIONS" minSdkVersion="33" /> </permission-set> + <permission-set name="virtual_device"> + <permission name="android.permission.CREATE_VIRTUAL_DEVICE" minSdkVersion="33" /> + <permission name="android.permission.ADD_TRUSTED_DISPLAY" minSdkVersion="33" /> + <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" minSdkVersion="33" /> + <permission + name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" + featureFlag="android.companion.virtualdevice.flags.Flags.activityControlApi" /> + </permission-set> + <role name="android.app.role.ASSISTANT" behavior="AssistantRoleBehavior" defaultHolders="config_defaultAssistant" description="@string/role_assistant_description" exclusive="true" + exclusivity="user" fallBackToDefaultHolder="true" showNone="true" label="@string/role_assistant_label" @@ -164,6 +174,7 @@ defaultHolders="config_defaultBrowser" description="@string/role_browser_description" exclusive="true" + exclusivity="user" label="@string/role_browser_label" overrideUserWhenGranting="true" requestDescription="@string/role_browser_request_description" @@ -207,6 +218,7 @@ defaultHolders="config_defaultDialer" description="@string/role_dialer_description" exclusive="true" + exclusivity="user" fallBackToDefaultHolder="true" label="@string/role_dialer_label" overrideUserWhenGranting="true" @@ -295,6 +307,7 @@ defaultHolders="config_defaultSms" description="@string/role_sms_description" exclusive="true" + exclusivity="user" label="@string/role_sms_label" overrideUserWhenGranting="true" requestDescription="@string/role_sms_request_description" @@ -385,6 +398,7 @@ behavior="EmergencyRoleBehavior" description="@string/role_emergency_description" exclusive="true" + exclusivity="user" label="@string/role_emergency_label" overrideUserWhenGranting="true" requestDescription="@string/role_emergency_request_description" @@ -417,6 +431,7 @@ behavior="HomeRoleBehavior" description="@string/role_home_description" exclusive="true" + exclusivity="user" label="@string/role_home_label" overrideUserWhenGranting="true" requestDescription="@string/role_home_request_description" @@ -463,6 +478,7 @@ defaultHolders="config_defaultCallRedirection" description="@string/role_call_redirection_description" exclusive="true" + exclusivity="user" label="@string/role_call_redirection_label" overrideUserWhenGranting="true" requestDescription="@string/role_call_redirection_request_description" @@ -484,6 +500,7 @@ defaultHolders="config_defaultCallScreening" description="@string/role_call_screening_description" exclusive="true" + exclusivity="user" label="@string/role_call_screening_label" overrideUserWhenGranting="true" requestDescription="@string/role_call_screening_request_description" @@ -509,6 +526,7 @@ name="android.app.role.SYSTEM_GALLERY" defaultHolders="config_systemGallery" exclusive="true" + exclusivity="user" static="true" systemOnly="true" visible="false"> @@ -528,6 +546,7 @@ behavior="v31.AutomotiveRoleBehavior" defaultHolders="config_systemAutomotiveCluster" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -545,6 +564,7 @@ behavior="v31.CompanionDeviceWatchRoleBehavior" description="@string/role_watch_description" exclusive="false" + exclusivity="none" minSdkVersion="31" systemOnly="false" visible="false"> @@ -573,6 +593,7 @@ name="android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION" defaultHolders="config_systemAutomotiveProjection" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -610,13 +631,13 @@ behavior="v31.SystemShellRoleBehavior" defaultHolders="config_systemShell" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" visible="false"> <permissions> <!-- Used for CTS testing --> - <permission name="android.permission.CREATE_VIRTUAL_DEVICE" minSdkVersion="33" /> <permission name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE" /> <permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" minSdkVersion="33"/> <permission name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION" /> @@ -633,8 +654,6 @@ minSdkVersion="33" /> <permission name="android.permission.MANAGE_SAFETY_CENTER" minSdkVersion="33" /> - <permission name="android.permission.ADD_TRUSTED_DISPLAY" minSdkVersion="33" /> - <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_POLICY_ACCOUNT_MANAGEMENT" @@ -697,6 +716,14 @@ minSdkVersion="35" /> <permission name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" minSdkVersion="35" /> + <permission name="android.permission.EXECUTE_APP_FUNCTIONS" + featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> + <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" + featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> + <permission name="android.permission.COPY_ACCOUNTS" + featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> + <permission name="android.permission.REMOVE_ACCOUNTS" + featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> </permissions> </role> @@ -704,6 +731,7 @@ name="android.app.role.SYSTEM_CONTACTS" defaultHolders="config_systemContacts" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -720,6 +748,7 @@ allowBypassingQualification="true" defaultHolders="config_systemSpeechRecognizer" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -741,6 +770,7 @@ name="android.app.role.SYSTEM_WIFI_COEX_MANAGER" defaultHolders="config_systemWifiCoexManager" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -755,6 +785,7 @@ name="android.app.role.SYSTEM_WELLBEING" defaultHolders="config_systemWellbeing" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -783,6 +814,7 @@ behavior="v31.TelevisionRoleBehavior" defaultHolders="config_systemTelevisionNotificationHandler" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -801,6 +833,7 @@ name="android.app.role.SYSTEM_COMPANION_DEVICE_PROVIDER" defaultHolders="config_systemCompanionDeviceProvider" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -822,7 +855,8 @@ ~ In addition, packages MUST NOT: ~ - Request INTERNET permission. Instead packages MUST access the internet through ~ well-defined APIs in an open source project. - ~ - Perform direct binds to other applications, except the following system packages: + ~ - Perform direct binds to other applications, except the following system packages or + ~ other preloaded packages conforming with the requirements here: ~ - Bluetooth ~ - Contacts ~ - Media @@ -836,6 +870,7 @@ name="android.app.role.SYSTEM_UI_INTELLIGENCE" defaultHolders="config_systemUiIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -860,6 +895,8 @@ <permission name="android.permission.READ_SMS" minSdkVersion="33" /> <permission name="android.permission.READ_PEOPLE_DATA" /> <permission name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" /> + <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" + featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> </permissions> </role> @@ -889,6 +926,7 @@ name="android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE" defaultHolders="config_systemAmbientAudioIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -935,6 +973,7 @@ name="android.app.role.SYSTEM_AUDIO_INTELLIGENCE" defaultHolders="config_systemAudioIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -981,6 +1020,7 @@ name="android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE" defaultHolders="config_systemNotificationIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1023,6 +1063,7 @@ name="android.app.role.SYSTEM_TEXT_INTELLIGENCE" defaultHolders="config_systemTextIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1063,6 +1104,7 @@ name="android.app.role.SYSTEM_VISUAL_INTELLIGENCE" defaultHolders="config_systemVisualIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1088,6 +1130,7 @@ name="android.app.role.SYSTEM_DOCUMENT_MANAGER" behavior="v33.DocumentManagerRoleBehavior" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1120,6 +1163,7 @@ allowBypassingQualification="true" defaultHolders="config_systemActivityRecognizer" exclusive="false" + exclusivity="none" static="true" systemOnly="true" visible="false"> @@ -1139,6 +1183,7 @@ name="android.app.role.SYSTEM_UI" defaultHolders="config_systemUi" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1165,6 +1210,7 @@ behavior="v31.TelevisionRoleBehavior" defaultHolders="config_systemTelevisionRemoteService" exclusive="true" + exclusivity="user" minSdkVersion="31" static="true" systemOnly="true" @@ -1184,17 +1230,18 @@ behavior="v33.CompanionDeviceAppStreamingRoleBehavior" description="@string/role_app_streaming_description" exclusive="false" + exclusivity="none" minSdkVersion="33" systemOnly="true" visible="false"> <permissions> <permission-set name="notifications" /> + <permission-set name="virtual_device" /> <!-- For capturing audio from the app on the device. --> <permission name="android.permission.RECORD_AUDIO" /> - <permission name="android.permission.CREATE_VIRTUAL_DEVICE" minSdkVersion="33" /> - <permission name="android.permission.ADD_TRUSTED_DISPLAY" minSdkVersion="33" /> - <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" minSdkVersion="33" /> - + <permission + name="android.permission.ADD_MIRROR_DISPLAY" + featureFlag="android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole" /> <!--TODO(b/201605314) For calling Telecom framework API for audio streaming--> <!--<permission name="android.permission.PROVIDE_CALL_ENDPOINTS" />--> </permissions> @@ -1210,6 +1257,7 @@ behavior="v33.CompanionDeviceComputerRoleBehavior" description="@string/role_companion_device_computer_description" exclusive="false" + exclusivity="none" minSdkVersion="33" systemOnly="true" visible="false"> @@ -1229,6 +1277,7 @@ name="android.app.role.COMPANION_DEVICE_GLASSES" behavior="v34.CompanionDeviceGlassesRoleBehavior" exclusive="false" + exclusivity="none" minSdkVersion="34" systemOnly="false" visible="false"> @@ -1255,14 +1304,15 @@ name="android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING" allowBypassingQualification="true" exclusive="false" + exclusivity="none" 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" /> + <permission-set name="virtual_device" /> + <permission-set name="notifications" + featureFlag="android.companion.virtualdevice.flags.Flags.notificationsForDeviceStreaming" /> </permissions> </role> @@ -1270,6 +1320,7 @@ name="android.app.role.SYSTEM_SUPERVISION" defaultHolders="config_systemSupervision" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1280,6 +1331,46 @@ <permission name="android.permission.MANAGE_DEFAULT_APPLICATIONS" minSdkVersion="34"/> <permission name="android.permission.SUSPEND_APPS"/> <permission name="android.permission.SYSTEM_APPLICATION_OVERLAY"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_FUN" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCATION" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_DEVICE_POLICY_TIME" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> + <permission name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" + featureFlag="android.permission.flags.Flags.supervisionRolePermissionUpdateEnabled"/> </permissions> </role> @@ -1292,6 +1383,7 @@ behavior="v33.DevicePolicyManagementRoleBehavior" defaultHolders="config_devicePolicyManagement" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="false" @@ -1397,6 +1489,12 @@ <permission name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" minSdkVersion="35" /> <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" minSdkVersion="35" /> <permission name="android.permission.MANAGE_DEVICE_POLICY_SMS" minSdkVersion="35" /> + <permission name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS" + featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> + <permission name="android.permission.COPY_ACCOUNTS" + featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> + <permission name="android.permission.REMOVE_ACCOUNTS" + featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> </permissions> </role> @@ -1404,6 +1502,7 @@ name="android.app.role.SYSTEM_APP_PROTECTION_SERVICE" defaultHolders="config_systemAppProtectionService" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1432,6 +1531,7 @@ behavior="v31.AutomotiveRoleBehavior" defaultHolders="config_systemAutomotiveCalendarSyncManager" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1453,6 +1553,7 @@ defaultHolders="config_defaultAutomotiveNavigation" description="@string/role_automotive_navigation_description" exclusive="true" + exclusivity="user" label="@string/role_automotive_navigation_label" minSdkVersion="33" overrideUserWhenGranting="true" @@ -1526,6 +1627,7 @@ name="android.app.role.SYSTEM_SETTINGS_INTELLIGENCE" defaultHolders="config_systemSettingsIntelligence" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1543,6 +1645,7 @@ name="android.app.role.SYSTEM_BLUETOOTH_STACK" defaultHolders="config_systemBluetoothStack" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1567,6 +1670,7 @@ <role name="android.app.role.FINANCED_DEVICE_KIOSK" exclusive="true" + exclusivity="user" minSdkVersion="34" visible="false"> <permissions> @@ -1582,6 +1686,7 @@ name="android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER" defaultHolders="config_systemFinancedDeviceController" exclusive="true" + exclusivity="user" minSdkVersion="34" static="true" systemOnly="true" @@ -1609,6 +1714,7 @@ behavior="v33.SystemWearHealthServiceRoleBehavior" defaultHolders="config_systemWearHealthService" exclusive="true" + exclusivity="user" minSdkVersion="33" static="true" systemOnly="true" @@ -1618,6 +1724,12 @@ <permission-set name="location" /> <permission name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <permission name="android.permission.ACTIVITY_RECOGNITION" /> + <permission + name="android.permission.health.READ_HEART_RATE" + featureFlag="android.permission.flags.Flags.replaceBodySensorPermissionEnabled" /> + <permission + name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" + featureFlag="android.permission.flags.Flags.replaceBodySensorPermissionEnabled" /> </permissions> </role> @@ -1630,6 +1742,7 @@ defaultHolders="config_defaultNotes" description="@string/role_notes_description" exclusive="true" + exclusivity="user" label="@string/role_notes_label" minSdkVersion="34" overrideUserWhenGranting="true" @@ -1671,6 +1784,7 @@ allowBypassingQualification="true" defaultHolders="config_systemCallStreaming" exclusive="true" + exclusivity="user" minSdkVersion="34" static="true" systemOnly="true" @@ -1693,6 +1807,7 @@ behavior="v35.RetailDemoRoleBehavior" defaultHolders="config_defaultRetailDemo" exclusive="true" + exclusivity="user" minSdkVersion="35" static="true" visible="false"> @@ -1719,6 +1834,7 @@ defaultHolders="config_defaultWallet" description="@string/role_wallet_description" exclusive="true" + exclusivity="user" label="@string/role_wallet_label" minSdkVersion="35" overrideUserWhenGranting="true" @@ -1729,5 +1845,85 @@ shortLabel="@string/role_wallet_short_label" uiBehavior="v35.WalletRoleUiBehavior"/> + <role + name="android.app.role.SYSTEM_DEPENDENCY_INSTALLER" + allowBypassingQualification="true" + defaultHolders="config_systemDependencyInstaller" + exclusive="true" + exclusivity="user" + featureFlag="android.content.pm.Flags.sdkDependencyInstaller" + static="true" + systemOnly="true" + visible="false"> + <required-components> + <service permission="android.permission.BIND_DEPENDENCY_INSTALLER"> + <intent-filter> + <action name="android.content.pm.action.INSTALL_DEPENDENCY" /> + </intent-filter> + </service> + </required-components> + <permissions> + <permission name="android.permission.ACCESS_SHARED_LIBRARIES" /> + <permission name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES" /> + </permissions> + </role> + <!--- + ~ A role for testing cross-user roles (exclusivity="profileGroup"). This should never be used + ~ to gate any actual functionality. + --> + <role + name="android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY" + behavior="ReservedForTestingProfileGroupExclusivityRoleBehavior" + description="@string/role_for_testing_profile_group_exclusivity_description" + exclusive="true" + exclusivity="profileGroup" + fallBackToDefaultHolder="true" + featureFlag="com.android.permission.flags.Flags.crossUserRoleEnabled" + label="@string/role_for_testing_profile_group_exclusivity_label" + requestable="true" + requestDescription="@string/role_for_testing_profile_group_exclusivity_request_description" + requestTitle="@string/role_for_testing_profile_group_exclusivity_request_title" + shortLabel="@string/role_for_testing_profile_group_exclusivity_short_label" + showNone="true" + visible="true"/> + + <!--- + ~ A role for the vendor package that provides privacy-preserving intelligent processor for + ~ vendor specific features. + ~ + ~ A package holding this role MUST comply with requirements outlined in the Android CDD + ~ section "9.8.6 Content Capture". + ~ Example link for Android 15: + ~ https://source.android.com/docs/compatibility/15/android-15-cdd#986_os-level_and_ambient_data + ~ + ~ In addition, packages MUST NOT: + ~ - Request INTERNET permission. Instead packages MUST access the internet through + ~ well-defined APIs in an open source project. + ~ - Perform direct binds to other applications, except the following system packages or + ~ other preloaded packages conforming with the requirements here: + ~ - Bluetooth + ~ - Contacts + ~ - Media + ~ - Telephony + ~ - System UI + ~ - Component providing internet APIs (see above) + ~ To achieve this packages MUST set up explicit <allow-association> configuration in the + ~ system config. + --> + <role + name="android.app.role.SYSTEM_VENDOR_INTELLIGENCE" + defaultHolders="config_systemVendorIntelligence" + exclusive="true" + exclusivity="user" + featureFlag="android.permission.flags.Flags.systemVendorIntelligenceRoleEnabled" + static="true" + systemOnly="true" + visible="false"> + <permissions> + <permission name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" /> + <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" + featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> + </permissions> + </role> </roles> diff --git a/PermissionController/role-controller/Android.bp b/PermissionController/role-controller/Android.bp index ea6545bb1..9f217660a 100644 --- a/PermissionController/role-controller/Android.bp +++ b/PermissionController/role-controller/Android.bp @@ -17,6 +17,9 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +// Any place that role-controller is added as a dependency must also include +// "com.android.permission.flags-aconfig-java" or +// "com.android.permission.flags-aconfig-java-export", java_library { name: "role-controller", srcs: [ @@ -24,11 +27,17 @@ java_library { ], libs: [ "androidx.annotation_annotation", + "com.android.permission.flags-aconfig-java", + "framework-annotations-lib", ], static_libs: [ "modules-utils-build_system", + "android.app.appfunctions.exported-flags-aconfig-java", + "android.companion.virtualdevice.flags-aconfig-java-export", + "android.content.pm.flags-aconfig-java-export", "android.permission.flags-aconfig-java-export", "android.os.flags-aconfig-java-export", + "device_policy_aconfig_flags_java_export", ], apex_available: [ "com.android.permission", diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java index c20734cde..d025d1b03 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java @@ -17,6 +17,7 @@ package com.android.role.controller.behavior; import android.app.ActivityManager; +import android.app.appfunctions.flags.Flags; import android.app.role.RoleManager; import android.content.Context; import android.content.Intent; @@ -36,15 +37,18 @@ import android.util.Xml; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.role.controller.model.Permissions; import com.android.role.controller.model.Role; import com.android.role.controller.model.RoleBehavior; import com.android.role.controller.model.VisibilityMixin; +import com.android.role.controller.util.PackageUtils; import com.android.role.controller.util.UserUtils; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; @@ -55,6 +59,14 @@ public class AssistantRoleBehavior implements RoleBehavior { private static final String LOG_TAG = AssistantRoleBehavior.class.getSimpleName(); + /** + * Permissions to be granted if the application fulfilling the assistant role is also a system + * or preinstalled app. + */ + private static final List<String> SYSTEM_ASSISTANT_PERMISSIONS = Arrays.asList( + android.Manifest.permission.EXECUTE_APP_FUNCTIONS + ); + @Override public void onRoleAddedAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { @@ -76,6 +88,28 @@ public class AssistantRoleBehavior implements RoleBehavior { return !UserUtils.isProfile(user, context); } + @Override + public void grantAsUser(@NonNull Role role, @NonNull String packageName, + @NonNull UserHandle user, @NonNull Context context) { + if (Flags.enableAppFunctionManager()) { + if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) { + Permissions.grantAsUser(packageName, SYSTEM_ASSISTANT_PERMISSIONS, false, false, + true, false, false, user, context); + } + } + } + + @Override + public void revokeAsUser(@NonNull Role role, @NonNull String packageName, + @NonNull UserHandle user, @NonNull Context context) { + if (Flags.enableAppFunctionManager()) { + if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) { + Permissions.revokeAsUser(packageName, SYSTEM_ASSISTANT_PERMISSIONS, true, false, + false, user, context); + } + } + } + @Nullable @Override public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user, diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java new file mode 100644 index 000000000..a9be00806 --- /dev/null +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.role.controller.behavior; + +import android.app.role.RoleManager; +import android.content.Context; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleBehavior; +import com.android.role.controller.util.RoleFlags; +import com.android.role.controller.util.UserUtils; + +import java.util.List; + +public class ReservedForTestingProfileGroupExclusivityRoleBehavior implements RoleBehavior { + @Nullable + @Override + public List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + if (RoleFlags.isProfileGroupExclusivityAvailable()) { + Context userContext = UserUtils.getUserContext(context, user); + RoleManager roleManager = userContext.getSystemService(RoleManager.class); + return roleManager.getDefaultHoldersForTest(role.getName()); + } else { + return null; + } + } + + @Override + public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + if (RoleFlags.isProfileGroupExclusivityAvailable()) { + Context userContext = UserUtils.getUserContext(context, user); + RoleManager roleManager = userContext.getSystemService(RoleManager.class); + return roleManager.isRoleVisibleForTest(role.getName()); + } else { + return false; + } + } +} diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java index 71f7795f7..382a03d5f 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java @@ -46,17 +46,12 @@ public class NotesRoleBehavior implements RoleBehavior { return false; } - // Cloned profile shouldn't have a separate role. - if (UserUtils.isCloneProfile(user, context)) { - return false; - } - if (UserUtils.isManagedProfile(user, context)) { // The role holder for work profile is separately controlled via config. return resources.getBoolean(android.R.bool.config_enableDefaultNotesForWorkProfile); } - return true; + return !UserUtils.isProfile(user, context); } /** diff --git a/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java b/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java index 8e5a768df..82873d185 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java +++ b/PermissionController/role-controller/java/com/android/role/controller/compat/AppOpsManagerCompat.java @@ -17,10 +17,13 @@ package com.android.role.controller.compat; +import android.annotation.SuppressLint; import android.app.AppOpsManager; +import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; @@ -29,6 +32,11 @@ import java.util.Objects; /** Helper for accessing features in {@link AppOpsManager}. */ public class AppOpsManagerCompat { + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @SuppressLint("NewApi") + public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS = + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS; + private AppOpsManagerCompat() {} /** diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java index 11cfff114..56c4944a0 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java @@ -23,11 +23,13 @@ import android.os.UserHandle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.role.controller.util.PackageUtils; import java.util.Objects; +import java.util.function.Supplier; /** * An app op to be granted or revoke by a {@link Role}. @@ -41,6 +43,12 @@ public class AppOp { private final String mName; /** + * The feature flag for this app op to be granted, or {@code null} if none. + */ + @Nullable + private final Supplier<Boolean> mFeatureFlag; + + /** * The maximum target SDK version for this app op to be granted, or {@code null} if none. */ @Nullable @@ -56,9 +64,10 @@ public class AppOp { */ private final int mMode; - public AppOp(@NonNull String name, @Nullable Integer maxTargetSdkVersion, int minSdkVersion, - int mode) { + public AppOp(@NonNull String name, @Nullable Supplier<Boolean> featureFlag, + @Nullable Integer maxTargetSdkVersion, int minSdkVersion, int mode) { mName = name; + mFeatureFlag = featureFlag; mMaxTargetSdkVersion = maxTargetSdkVersion; mMinSdkVersion = minSdkVersion; mMode = mode; @@ -70,11 +79,16 @@ public class AppOp { } @Nullable + public Supplier<Boolean> getFeatureFlag() { + return mFeatureFlag; + } + + @Nullable public Integer getMaxTargetSdkVersion() { return mMaxTargetSdkVersion; } - public int getMindSdkVersion() { + public int getMinSdkVersion() { return mMinSdkVersion; } @@ -117,11 +131,19 @@ public class AppOp { return Permissions.setAppOpUidModeAsUser(packageName, mName, defaultMode, user, context); } + @VisibleForTesting + public boolean isAvailableByFeatureFlagAndSdkVersion() { + if (mFeatureFlag != null && !mFeatureFlag.get()) { + return false; + } + return Build.VERSION.SDK_INT >= mMinSdkVersion + // Workaround to match the value 35 for V in roles.xml before SDK finalization. + || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV()); + } + private boolean isAvailableAsUser(@NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { - if (!(Build.VERSION.SDK_INT >= mMinSdkVersion - // Workaround to match the value 35 for V in roles.xml before SDK finalization. - || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV()))) { + if (!isAvailableByFeatureFlagAndSdkVersion()) { return false; } if (mMaxTargetSdkVersion == null) { @@ -139,6 +161,7 @@ public class AppOp { public String toString() { return "AppOp{" + "mName='" + mName + '\'' + + ", mFeatureFlag=" + mFeatureFlag + ", mMaxTargetSdkVersion=" + mMaxTargetSdkVersion + ", mMinSdkVersion=" + mMinSdkVersion + ", mMode=" + mMode @@ -153,15 +176,16 @@ public class AppOp { if (object == null || getClass() != object.getClass()) { return false; } - AppOp appOp = (AppOp) object; - return mMinSdkVersion == appOp.mMinSdkVersion - && mMode == appOp.mMode - && Objects.equals(mName, appOp.mName) - && Objects.equals(mMaxTargetSdkVersion, appOp.mMaxTargetSdkVersion); + AppOp that = (AppOp) object; + return mMinSdkVersion == that.mMinSdkVersion + && mMode == that.mMode + && Objects.equals(mName, that.mName) + && Objects.equals(mFeatureFlag, that.mFeatureFlag) + && Objects.equals(mMaxTargetSdkVersion, that.mMaxTargetSdkVersion); } @Override public int hashCode() { - return Objects.hash(mName, mMaxTargetSdkVersion, mMinSdkVersion, mMode); + return Objects.hash(mName, mFeatureFlag, mMaxTargetSdkVersion, mMinSdkVersion, mMode); } } diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java index f999e5972..05b19ff94 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java @@ -23,11 +23,13 @@ import android.os.Build; import android.os.UserHandle; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; import com.android.role.controller.util.UserUtils; import java.util.Objects; +import java.util.function.Supplier; /** * A permission to be granted or revoke by a {@link Role}. @@ -41,6 +43,12 @@ public class Permission { private final String mName; /** + * The feature flag for this permission to be granted, or {@code null} if none. + */ + @Nullable + private final Supplier<Boolean> mFeatureFlag; + + /** * The minimum SDK version for this permission to be granted. */ private final int mMinSdkVersion; @@ -50,8 +58,10 @@ public class Permission { */ private final int mOptionalMinSdkVersion; - public Permission(@NonNull String name, int minSdkVersion, int optionalMinSdkVersion) { + public Permission(@NonNull String name, @Nullable Supplier<Boolean> featureFlag, + int minSdkVersion, int optionalMinSdkVersion) { mName = name; + mFeatureFlag = featureFlag; mMinSdkVersion = minSdkVersion; mOptionalMinSdkVersion = optionalMinSdkVersion; } @@ -61,6 +71,11 @@ public class Permission { return mName; } + @Nullable + public Supplier<Boolean> getFeatureFlag() { + return mFeatureFlag; + } + public int getMinSdkVersion() { return mMinSdkVersion; } @@ -78,6 +93,9 @@ public class Permission { * @return whether this permission is available */ public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) { + if (mFeatureFlag != null && !mFeatureFlag.get()) { + return false; + } if (Build.VERSION.SDK_INT >= mMinSdkVersion // Workaround to match the value 35 for V in roles.xml before SDK finalization. || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV())) { @@ -110,17 +128,20 @@ public class Permission { * @return a permission with the specified SDK versions */ @NonNull - public Permission withSdkVersions(int minSdkVersion, int optionalMinSdkVersion) { - if (mMinSdkVersion == minSdkVersion && mOptionalMinSdkVersion == optionalMinSdkVersion) { + public Permission withFeatureFlagAndSdkVersions(@Nullable Supplier<Boolean> featureFlag, + int minSdkVersion, int optionalMinSdkVersion) { + if (mFeatureFlag == featureFlag && mMinSdkVersion == minSdkVersion + && mOptionalMinSdkVersion == optionalMinSdkVersion) { return this; } - return new Permission(mName, minSdkVersion, optionalMinSdkVersion); + return new Permission(mName, featureFlag, minSdkVersion, optionalMinSdkVersion); } @Override public String toString() { return "Permission{" + "mName='" + mName + '\'' + + ", mFeatureFlag=" + mFeatureFlag + ", mMinSdkVersion=" + mMinSdkVersion + ", mOptionalMinSdkVersion=" + mOptionalMinSdkVersion + '}'; @@ -137,11 +158,12 @@ public class Permission { Permission that = (Permission) object; return mMinSdkVersion == that.mMinSdkVersion && mOptionalMinSdkVersion == that.mOptionalMinSdkVersion - && mName.equals(that.mName); + && Objects.equals(mName, that.mName) + && Objects.equals(mFeatureFlag, that.mFeatureFlag); } @Override public int hashCode() { - return Objects.hash(mName, mMinSdkVersion, mOptionalMinSdkVersion); + return Objects.hash(mName, mFeatureFlag, mMinSdkVersion, mOptionalMinSdkVersion); } } diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java b/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java index e788fdce1..820ff3d4e 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java @@ -51,11 +51,13 @@ public class Permissions { private static final boolean DEBUG = false; + private static final Object sPermissionInfoLock = new Object(); + private static final ArrayMap<String, Boolean> sIsRuntimePermission = new ArrayMap<>(); + private static final ArrayMap<String, Boolean> sIsRestrictedPermission = new ArrayMap<>(); + + private static final Object sForegroundBackgroundPermissionMappingsLock = new Object(); private static ArrayMap<String, String> sForegroundToBackgroundPermission; private static ArrayMap<String, List<String>> sBackgroundToForegroundPermissions; - private static final Object sForegroundBackgroundPermissionMappingsLock = new Object(); - - private static final ArrayMap<String, Boolean> sRestrictedPermissions = new ArrayMap<>(); /** * Filter a list of permissions based on their SDK versions. @@ -86,8 +88,9 @@ public class Permissions { * * @param packageName the package name of the application to be granted permissions to * @param permissions the list of permissions to be granted - * @param overrideDisabledSystemPackage whether to ignore the permissions of a disabled system - * package (if this package is an updated system package) + * @param ignoreDisabledSystemPackage whether to ignore the requested permissions of a disabled + * system package (if this package is an updated system + * package) when granting runtime permissions * @param overrideUserSetAndFixed whether to override user set and fixed flags on the permission * @param setGrantedByRole whether the permissions will be granted as granted-by-role * @param setGrantedByDefault whether the permissions will be granted as granted-by-default @@ -101,7 +104,7 @@ public class Permissions { * PackageInfo, java.util.Set, boolean, boolean, int) */ public static boolean grantAsUser(@NonNull String packageName, - @NonNull List<String> permissions, boolean overrideDisabledSystemPackage, + @NonNull List<String> permissions, boolean ignoreDisabledSystemPackage, boolean overrideUserSetAndFixed, boolean setGrantedByRole, boolean setGrantedByDefault, boolean setSystemFixed, @NonNull UserHandle user, @NonNull Context context) { if (setGrantedByRole == setGrantedByDefault) { @@ -145,15 +148,17 @@ public class Permissions { // choice to grant this app the permissions needed to function. For all other // apps, (default grants on first boot and user creation) we don't grant default // permissions if the version on the system image does not declare them. - if (!overrideDisabledSystemPackage && isUpdatedSystemApp(packageInfo)) { + if (!ignoreDisabledSystemPackage && isUpdatedSystemApp(packageInfo)) { PackageInfo disabledSystemPackageInfo = getFactoryPackageInfoAsUser(packageName, user, context); if (disabledSystemPackageInfo != null) { - if (ArrayUtils.isEmpty(disabledSystemPackageInfo.requestedPermissions)) { - return false; + for (int i = permissionsToGrant.size() - 1; i >= 0; i--) { + String permission = permissionsToGrant.valueAt(i); + if (isRuntimePermission(permission, context) && !ArrayUtils.contains( + disabledSystemPackageInfo.requestedPermissions, permission)) { + permissionsToGrant.removeAt(i); + } } - CollectionUtils.retainAll(permissionsToGrant, - disabledSystemPackageInfo.requestedPermissions); if (permissionsToGrant.isEmpty()) { return false; } @@ -258,7 +263,8 @@ public class Permissions { if (!wasPermissionOrAppOpGranted) { // If we've granted a permission which wasn't granted, it's no longer user set or fixed. newMask |= PackageManager.FLAG_PERMISSION_USER_FIXED - | PackageManager.FLAG_PERMISSION_USER_SET; + | PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_ONE_TIME; } // If a component gets a permission for being the default handler A and also default handler // B, we grant the weaker grant form. This only applies to default permission grant. @@ -629,7 +635,8 @@ public class Permissions { } if (!overrideUserSetAndFixed) { fixedFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED - | PackageManager.FLAG_PERMISSION_USER_SET; + | PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_ONE_TIME; } return (flags & fixedFlags) != 0; } @@ -701,6 +708,50 @@ public class Permissions { return true; } + private static boolean isRuntimePermission(@NonNull String permission, + @NonNull Context context) { + synchronized (sPermissionInfoLock) { + Boolean isRuntimePermission = sIsRuntimePermission.get(permission); + if (isRuntimePermission != null) { + return isRuntimePermission; + } + fetchPermissionInfoLocked(permission, context); + return sIsRuntimePermission.get(permission); + } + } + + private static boolean isRestrictedPermission(@NonNull String permission, + @NonNull Context context) { + synchronized (sPermissionInfoLock) { + Boolean isRestrictedPermission = sIsRestrictedPermission.get(permission); + if (isRestrictedPermission != null) { + return isRestrictedPermission; + } + fetchPermissionInfoLocked(permission, context); + return sIsRestrictedPermission.get(permission); + } + } + + private static void fetchPermissionInfoLocked(@NonNull String permission, + @NonNull Context context) { + PackageManager packageManager = context.getPackageManager(); + PermissionInfo permissionInfo = null; + try { + permissionInfo = packageManager.getPermissionInfo(permission, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "Cannot get PermissionInfo for permission: " + permission); + } + + // Don't expect that to be a transient error, so we can still cache the failed information. + boolean isRuntimePermission = permissionInfo != null + && permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS; + boolean isRestrictedPermission = permissionInfo != null + && (permissionInfo.flags & (PermissionInfo.FLAG_SOFT_RESTRICTED + | PermissionInfo.FLAG_HARD_RESTRICTED)) != 0; + sIsRuntimePermission.put(permission, isRuntimePermission); + sIsRestrictedPermission.put(permission, isRestrictedPermission); + } + private static boolean isForegroundPermission(@NonNull String permission, @NonNull Context context) { ensureForegroundBackgroundPermissionMappings(context); @@ -731,40 +782,13 @@ public class Permissions { synchronized (sForegroundBackgroundPermissionMappingsLock) { if (sForegroundToBackgroundPermission == null && sBackgroundToForegroundPermissions == null) { - createForegroundBackgroundPermissionMappings(context); + createForegroundBackgroundPermissionMappingsLocked(context); } } } - private static boolean isRestrictedPermission(@NonNull String permission, + private static void createForegroundBackgroundPermissionMappingsLocked( @NonNull Context context) { - synchronized (sRestrictedPermissions) { - if (sRestrictedPermissions.containsKey(permission)) { - return sRestrictedPermissions.get(permission); - } - } - - PackageManager packageManager = context.getPackageManager(); - PermissionInfo permissionInfo = null; - try { - permissionInfo = packageManager.getPermissionInfo(permission, 0); - } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Cannot get PermissionInfo for permission: " + permission); - } - - // Don't expect that to be a transient error, so we can still cache the failed information. - boolean isRestrictedPermission = permissionInfo != null - && (permissionInfo.flags & (PermissionInfo.FLAG_SOFT_RESTRICTED - | PermissionInfo.FLAG_HARD_RESTRICTED)) != 0; - - synchronized (sRestrictedPermissions) { - sRestrictedPermissions.put(permission, isRestrictedPermission); - } - - return isRestrictedPermission; - } - - private static void createForegroundBackgroundPermissionMappings(@NonNull Context context) { List<String> permissions = new ArrayList<>(); sBackgroundToForegroundPermissions = new ArrayMap<>(); diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java index c3541c83b..02fa0d455 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java @@ -37,21 +37,28 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.SparseBooleanArray; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import androidx.annotation.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.role.controller.util.CollectionUtils; import com.android.role.controller.util.PackageUtils; +import com.android.role.controller.util.RoleFlags; import com.android.role.controller.util.RoleManagerCompat; import com.android.role.controller.util.UserUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; /** * Specifies a role and its properties. @@ -81,6 +88,36 @@ public class Role { private static final String CERTIFICATE_SEPARATOR = ":"; + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + EXCLUSIVITY_NONE, + EXCLUSIVITY_USER, + EXCLUSIVITY_PROFILE_GROUP + }) + public @interface Exclusivity {} + + /** + * Does not enforce any exclusivity, which means multiple apps may hold this role in a user. + */ + public static final int EXCLUSIVITY_NONE = 0; + + /** Enforces exclusivity within one user. */ + public static final int EXCLUSIVITY_USER = 1; + + /** + * Enforces exclusivity across all users (including profile users) in the same profile group. + */ + public static final int EXCLUSIVITY_PROFILE_GROUP = 2; + + /** Set of valid exclusivity values. */ + private static final SparseBooleanArray sExclusivityValues = new SparseBooleanArray(); + static { + sExclusivityValues.put(EXCLUSIVITY_NONE, true); + sExclusivityValues.put(EXCLUSIVITY_USER, true); + sExclusivityValues.put(EXCLUSIVITY_PROFILE_GROUP, + RoleFlags.isProfileGroupExclusivityAvailable()); + } + /** * The name of this role. Must be unique. */ @@ -108,9 +145,10 @@ public class Role { private final int mDescriptionResource; /** - * Whether this role is exclusive, i.e. allows at most one holder. + * The exclusivity of this role, i.e. whether this role allows multiple holders, or allows at + * most one holder within a user or a profile group. */ - private final boolean mExclusive; + private final int mExclusivity; /** * Whether this role should fall back to the default holder. @@ -118,6 +156,12 @@ public class Role { private final boolean mFallBackToDefaultHolder; /** + * The feature flag for this app op to be granted, or {@code null} if none. + */ + @Nullable + private final Supplier<Boolean> mFeatureFlag; + + /** * The string resource for the label of this role. */ @StringRes @@ -178,8 +222,8 @@ public class Role { /** * Whether the UI for this role will show the "None" item. Only valid if this role is - * {@link #mExclusive exclusive}, and {@link #getFallbackHolder(Context)} should also return - * empty to allow actually selecting "None". + * {@link #isExclusive()}, and {@link #getFallbackHolder(Context)} should + * also return empty to allow actually selecting "None". */ private final boolean mShowNone; @@ -233,7 +277,8 @@ public class Role { public Role(@NonNull String name, boolean allowBypassingQualification, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, - @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, + @StringRes int descriptionResource, @Exclusivity int exclusivity, + boolean fallBackToDefaultHolder, @Nullable Supplier<Boolean> featureFlag, @StringRes int labelResource, int maxSdkVersion, int minSdkVersion, boolean onlyGrantWhenAdded, boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, @@ -248,8 +293,9 @@ public class Role { mBehavior = behavior; mDefaultHoldersResourceName = defaultHoldersResourceName; mDescriptionResource = descriptionResource; - mExclusive = exclusive; + mExclusivity = exclusivity; mFallBackToDefaultHolder = fallBackToDefaultHolder; + mFeatureFlag = featureFlag; mLabelResource = labelResource; mMaxSdkVersion = maxSdkVersion; mMinSdkVersion = minSdkVersion; @@ -288,7 +334,34 @@ public class Role { } public boolean isExclusive() { - return mExclusive; + return getExclusivity() != EXCLUSIVITY_NONE; + } + + @Exclusivity + public int getExclusivity() { + if (com.android.permission.flags.Flags.crossUserRoleEnabled() && mBehavior != null) { + Integer exclusivity = mBehavior.getExclusivity(); + if (exclusivity != null) { + if (!sExclusivityValues.get(exclusivity)) { + throw new IllegalArgumentException("Invalid exclusivity: " + exclusivity); + } + if (mShowNone && exclusivity == EXCLUSIVITY_NONE) { + throw new IllegalArgumentException( + "Role cannot be non-exclusive when showNone is true: " + exclusivity); + } + if (!mPreferredActivities.isEmpty() && exclusivity == EXCLUSIVITY_PROFILE_GROUP) { + throw new IllegalArgumentException( + "Role cannot have preferred activities when exclusivity is profileGroup"); + } + return exclusivity; + } + } + return mExclusivity; + } + + @Nullable + public Supplier<Boolean> getFeatureFlag() { + return mFeatureFlag; } @StringRes @@ -396,11 +469,28 @@ public class Role { * @return whether this role is available. */ public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) { - if (!isAvailableBySdkVersion()) { + if (!isAvailableByFeatureFlagAndSdkVersion()) { + return false; + } + + if (getExclusivity() == EXCLUSIVITY_PROFILE_GROUP + && UserUtils.isPrivateProfile(user, context)) { return false; } + if (mBehavior != null) { - return mBehavior.isAvailableAsUser(this, user, context); + boolean isAvailableAsUser = mBehavior.isAvailableAsUser(this, user, context); + // Ensure that cross-user role is only available if also available for + // the profile-group's full user + if (isAvailableAsUser && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) { + UserHandle profileParent = UserUtils.getProfileParentOrSelf(user, context); + if (!Objects.equals(profileParent, user) + && !mBehavior.isAvailableAsUser(this, profileParent, context)) { + throw new IllegalArgumentException("Role is not available for profile parent: " + + profileParent.getIdentifier()); + } + } + return isAvailableAsUser; } return true; } @@ -410,7 +500,11 @@ public class Role { * * @return whether this role is available based on SDK version */ - boolean isAvailableBySdkVersion() { + @VisibleForTesting + public boolean isAvailableByFeatureFlagAndSdkVersion() { + if (mFeatureFlag != null && !mFeatureFlag.get()) { + return false; + } return (Build.VERSION.SDK_INT >= mMinSdkVersion // Workaround to match the value 35 for V in roles.xml before SDK finalization. || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV())) @@ -432,6 +526,12 @@ public class Role { @NonNull public List<String> getDefaultHoldersAsUser(@NonNull UserHandle user, @NonNull Context context) { + // Do not allow default role holder for non-active user if the role is exclusive to profile + // group + if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) { + return Collections.emptyList(); + } + if (mBehavior != null) { List<String> defaultHolders = mBehavior.getDefaultHoldersAsUser(this, user, context); if (defaultHolders != null) { @@ -543,6 +643,10 @@ public class Role { if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, user, context)) { return null; } + // Do not fall back for non-active user if the role is exclusive to profile group + if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) { + return null; + } if (mFallBackToDefaultHolder) { return CollectionUtils.firstOrNull(getDefaultHoldersAsUser(user, context)); } @@ -552,6 +656,17 @@ public class Role { return null; } + private boolean isNonActiveUserForProfileGroupExclusiveRole(@NonNull UserHandle user, + @NonNull Context context) { + if (RoleFlags.isProfileGroupExclusivityAvailable() + && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) { + Context userContext = UserUtils.getUserContext(context, user); + RoleManager userRoleManager = userContext.getSystemService(RoleManager.class); + return !Objects.equals(userRoleManager.getActiveUserForRole(mName), user); + } + return false; + } + /** * Check whether this role is allowed to bypass qualification, if enabled globally. * @@ -1022,7 +1137,7 @@ public class Role { */ @Nullable public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { - if (SdkLevel.isAtLeastU() && mExclusive) { + if (SdkLevel.isAtLeastU() && isExclusive()) { UserManager userManager = context.getSystemService(UserManager.class); if (userManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user)) { @@ -1085,8 +1200,9 @@ public class Role { + ", mBehavior=" + mBehavior + ", mDefaultHoldersResourceName=" + mDefaultHoldersResourceName + ", mDescriptionResource=" + mDescriptionResource - + ", mExclusive=" + mExclusive + + ", mExclusivity=" + mExclusivity + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder + + ", mFeatureFlag=" + mFeatureFlag + ", mLabelResource=" + mLabelResource + ", mMaxSdkVersion=" + mMaxSdkVersion + ", mMinSdkVersion=" + mMinSdkVersion diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java index 3849a50e3..86ca8e2ce 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java @@ -32,6 +32,14 @@ import java.util.List; public interface RoleBehavior { /** + * @see Role#getExclusivity() + */ + @Nullable + default Integer getExclusivity() { + return null; + } + + /** * @see Role#onRoleAddedAsUser(UserHandle, Context) */ default void onRoleAddedAsUser(@NonNull Role role, @NonNull UserHandle user, diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java index 30a6f1638..4b05554e3 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java @@ -19,12 +19,9 @@ package com.android.role.controller.model; import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Build; -import android.os.Process; import android.permission.flags.Flags; import android.util.ArrayMap; import android.util.Log; @@ -41,12 +38,15 @@ import com.android.role.controller.util.ResourceUtils; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; +import java.util.function.Supplier; /** * Parser for {@link Role} definitions. @@ -89,7 +89,9 @@ public class RoleParser { private static final String ATTRIBUTE_DEFAULT_HOLDERS = "defaultHolders"; private static final String ATTRIBUTE_DESCRIPTION = "description"; private static final String ATTRIBUTE_EXCLUSIVE = "exclusive"; + private static final String ATTRIBUTE_EXCLUSIVITY = "exclusivity"; private static final String ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER = "fallBackToDefaultHolder"; + private static final String ATTRIBUTE_FEATURE_FLAG = "featureFlag"; 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"; @@ -134,19 +136,38 @@ public class RoleParser { sModeNameToMode.put(MODE_NAME_FOREGROUND, AppOpsManager.MODE_FOREGROUND); } + private static final String EXCLUSIVITY_NONE = "none"; + private static final String EXCLUSIVITY_USER = "user"; + private static final String EXCLUSIVITY_PROFILE_GROUP = "profileGroup"; + + private static final Supplier<Boolean> sFeatureFlagFallback = () -> false; + + private static final ArrayMap<Class<?>, Class<?>> sPrimitiveToWrapperClass = new ArrayMap<>(); + static { + sPrimitiveToWrapperClass.put(Boolean.TYPE, Boolean.class); + sPrimitiveToWrapperClass.put(Byte.TYPE, Byte.class); + sPrimitiveToWrapperClass.put(Character.TYPE, Character.class); + sPrimitiveToWrapperClass.put(Short.TYPE, Short.class); + sPrimitiveToWrapperClass.put(Integer.TYPE, Integer.class); + sPrimitiveToWrapperClass.put(Long.TYPE, Long.class); + sPrimitiveToWrapperClass.put(Float.TYPE, Float.class); + sPrimitiveToWrapperClass.put(Double.TYPE, Double.class); + sPrimitiveToWrapperClass.put(Void.TYPE, Void.class); + } + @NonNull private final Context mContext; - private final boolean mValidationEnabled; + private final boolean mThrowOnError; public RoleParser(@NonNull Context context) { this(context, false); } @VisibleForTesting - public RoleParser(@NonNull Context context, boolean validationEnabled) { + public RoleParser(@NonNull Context context, boolean throwOnError) { mContext = context; - mValidationEnabled = validationEnabled; + mThrowOnError = throwOnError; } /** @@ -156,18 +177,21 @@ public class RoleParser { */ @NonNull public ArrayMap<String, Role> parse() { + Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseRolesXml(); + if (xml == null) { + return new ArrayMap<>(); + } + return xml.second; + } + + @Nullable + @VisibleForTesting + public Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> parseRolesXml() { try (XmlResourceParser parser = getRolesXml()) { - Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser); - if (xml == null) { - return new ArrayMap<>(); - } - ArrayMap<String, PermissionSet> permissionSets = xml.first; - ArrayMap<String, Role> roles = xml.second; - validateResult(permissionSets, roles); - return roles; + return parseXml(parser); } catch (XmlPullParserException | IOException e) { throwOrLogMessage("Unable to parse roles.xml", e); - return new ArrayMap<>(); + return null; } } @@ -273,6 +297,9 @@ public class RoleParser { return null; } + Supplier<Boolean> featureFlag = getAttributeMethodValue(parser, ATTRIBUTE_FEATURE_FLAG, + boolean.class, sFeatureFlagFallback, TAG_PERMISSION_SET); + int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION, Build.VERSION_CODES.BASE); int optionalMinSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION, @@ -295,11 +322,13 @@ public class RoleParser { if (permission == null) { continue; } + Supplier<Boolean> mergedFeatureFlag = + mergeFeatureFlags(permission.getFeatureFlag(), featureFlag); int mergedMinSdkVersion = Math.max(permission.getMinSdkVersion(), minSdkVersion); int mergedOptionalMinSdkVersion = Math.max(permission.getOptionalMinSdkVersion(), optionalMinSdkVersion); - permission = permission.withSdkVersions(mergedMinSdkVersion, - mergedOptionalMinSdkVersion); + permission = permission.withFeatureFlagAndSdkVersions(mergedFeatureFlag, + mergedMinSdkVersion, mergedOptionalMinSdkVersion); validateNoDuplicateElement(permission, permissions, "permission"); permissions.add(permission); } else { @@ -319,11 +348,16 @@ public class RoleParser { skipCurrentTag(parser); return null; } + + Supplier<Boolean> featureFlag = getAttributeMethodValue(parser, ATTRIBUTE_FEATURE_FLAG, + boolean.class, sFeatureFlagFallback, TAG_PERMISSION); + int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION, Build.VERSION_CODES.BASE); int optionalMinSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_OPTIONAL_MIN_SDK_VERSION, minSdkVersion); - return new Permission(name, minSdkVersion, optionalMinSdkVersion); + + return new Permission(name, featureFlag, minSdkVersion, optionalMinSdkVersion); } @Nullable @@ -384,16 +418,51 @@ public class RoleParser { shortLabelResource = 0; } - Boolean exclusive = requireAttributeBooleanValue(parser, ATTRIBUTE_EXCLUSIVE, true, - TAG_ROLE); - if (exclusive == null) { - skipCurrentTag(parser); - return null; + int exclusivity; + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + String exclusivityName = requireAttributeValue(parser, ATTRIBUTE_EXCLUSIVITY, TAG_ROLE); + if (exclusivityName == null) { + skipCurrentTag(parser); + return null; + } + switch (exclusivityName) { + case EXCLUSIVITY_NONE: + exclusivity = Role.EXCLUSIVITY_NONE; + break; + case EXCLUSIVITY_USER: + exclusivity = Role.EXCLUSIVITY_USER; + break; + case EXCLUSIVITY_PROFILE_GROUP: + // TODO(b/372743073): change to isAtLeastB once available + // EXCLUSIVITY_PROFILE behavior only available for B+ + // fallback to default of EXCLUSIVITY_USER + exclusivity = SdkLevel.isAtLeastV() + ? Role.EXCLUSIVITY_PROFILE_GROUP + : Role.EXCLUSIVITY_USER; + break; + default: + throwOrLogMessage("Invalid value for \"exclusivity\" on <role>: " + name + + ", exclusivity: " + exclusivityName); + skipCurrentTag(parser); + return null; + } + } else { + Boolean exclusive = + requireAttributeBooleanValue(parser, ATTRIBUTE_EXCLUSIVE, true, TAG_ROLE); + if (exclusive == null) { + skipCurrentTag(parser); + return null; + } + exclusivity = exclusive ? Role.EXCLUSIVITY_USER : Role.EXCLUSIVITY_NONE; } + boolean fallBackToDefaultHolder = getAttributeBooleanValue(parser, ATTRIBUTE_FALL_BACK_TO_DEFAULT_HOLDER, false); + Supplier<Boolean> featureFlag = getAttributeMethodValue(parser, ATTRIBUTE_FEATURE_FLAG, + boolean.class, sFeatureFlagFallback, TAG_ROLE); + int maxSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MAX_SDK_VERSION, Build.VERSION_CODES.CUR_DEVELOPMENT); int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION, @@ -438,7 +507,7 @@ public class RoleParser { 0); boolean showNone = getAttributeBooleanValue(parser, ATTRIBUTE_SHOW_NONE, false); - if (showNone && !exclusive) { + if (showNone && exclusivity == Role.EXCLUSIVITY_NONE) { throwOrLogMessage("showNone=\"true\" is invalid for a non-exclusive role: " + name); skipCurrentTag(parser); return null; @@ -511,6 +580,12 @@ public class RoleParser { skipCurrentTag(parser); continue; } + if (exclusivity == Role.EXCLUSIVITY_PROFILE_GROUP) { + throwOrLogMessage("<preferred-activities> is not supported for a" + + " profile-group-exclusive role: " + name); + skipCurrentTag(parser); + continue; + } preferredActivities = parsePreferredActivities(parser); break; default: @@ -535,12 +610,12 @@ public class RoleParser { preferredActivities = Collections.emptyList(); } return new Role(name, allowBypassingQualification, behavior, defaultHoldersResourceName, - descriptionResource, exclusive, fallBackToDefaultHolder, labelResource, - maxSdkVersion, minSdkVersion, onlyGrantWhenAdded, overrideUserWhenGranting, - requestDescriptionResource, requestTitleResource, requestable, - searchKeywordsResource, shortLabelResource, showNone, statik, systemOnly, visible, - requiredComponents, permissions, appOpPermissions, appOps, preferredActivities, - uiBehaviorName); + descriptionResource, exclusivity, fallBackToDefaultHolder, featureFlag, + labelResource, maxSdkVersion, minSdkVersion, onlyGrantWhenAdded, + overrideUserWhenGranting, requestDescriptionResource, requestTitleResource, + requestable, searchKeywordsResource, shortLabelResource, showNone, statik, + systemOnly, visible, requiredComponents, permissions, appOpPermissions, appOps, + preferredActivities, uiBehaviorName); } @NonNull @@ -592,7 +667,7 @@ public class RoleParser { int queryFlags = getAttributeIntValue(parser, ATTRIBUTE_QUERY_FLAGS, 0); IntentFilterData intentFilterData = null; List<RequiredMetaData> metaData = new ArrayList<>(); - List<String> validationMetaDataNames = mValidationEnabled ? new ArrayList<>() : null; + List<String> validationMetaDataNames = mThrowOnError ? new ArrayList<>() : null; int type; int depth; @@ -619,7 +694,7 @@ public class RoleParser { if (metaDataName == null) { continue; } - if (mValidationEnabled) { + if (mThrowOnError) { validateNoDuplicateElement(metaDataName, validationMetaDataNames, "meta data"); } @@ -636,7 +711,7 @@ public class RoleParser { RequiredMetaData requiredMetaData = new RequiredMetaData(metaDataName, metaDataValue, metaDataProhibited); metaData.add(requiredMetaData); - if (mValidationEnabled) { + if (mThrowOnError) { validationMetaDataNames.add(metaDataName); } break; @@ -780,6 +855,9 @@ public class RoleParser { throwOrLogMessage("Unknown permission set:" + permissionSetName); continue; } + Supplier<Boolean> featureFlag = getAttributeMethodValue(parser, + ATTRIBUTE_FEATURE_FLAG, boolean.class, sFeatureFlagFallback, + TAG_PERMISSION_SET); int minSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MIN_SDK_VERSION, Build.VERSION_CODES.BASE); int optionalMinSdkVersion = getAttributeIntValue(parser, @@ -789,12 +867,14 @@ public class RoleParser { for (int permissionsInSetIndex = 0; permissionsInSetIndex < permissionsInSetSize; permissionsInSetIndex++) { Permission permission = permissionsInSet.get(permissionsInSetIndex); + Supplier<Boolean> mergedFeatureFlag = + mergeFeatureFlags(permission.getFeatureFlag(), featureFlag); int mergedMinSdkVersion = Math.max(permission.getMinSdkVersion(), minSdkVersion); int mergedOptionalMinSdkVersion = Math.max( permission.getOptionalMinSdkVersion(), optionalMinSdkVersion); - permission = permission.withSdkVersions(mergedMinSdkVersion, - mergedOptionalMinSdkVersion); + permission = permission.withFeatureFlagAndSdkVersions(mergedFeatureFlag, + mergedMinSdkVersion, mergedOptionalMinSdkVersion); // We do allow intersection between permission sets. permissions.add(permission); } @@ -872,6 +952,8 @@ public class RoleParser { } validateNoDuplicateElement(name, appOpNames, "app op"); appOpNames.add(name); + Supplier<Boolean> featureFlag = getAttributeMethodValue(parser, + ATTRIBUTE_FEATURE_FLAG, boolean.class, sFeatureFlagFallback, TAG_APP_OP); Integer maxTargetSdkVersion = getAttributeIntValue(parser, ATTRIBUTE_MAX_TARGET_SDK_VERSION, Integer.MIN_VALUE); if (maxTargetSdkVersion == Integer.MIN_VALUE) { @@ -893,7 +975,8 @@ public class RoleParser { continue; } int mode = sModeNameToMode.valueAt(modeIndex); - AppOp appOp = new AppOp(name, maxTargetSdkVersion, minSdkVersion, mode); + AppOp appOp = new AppOp(name, featureFlag, maxTargetSdkVersion, minSdkVersion, + mode); appOps.add(appOp); } else { throwOrLogForUnknownTag(parser); @@ -1052,191 +1135,134 @@ public class RoleParser { return getAttributeResourceValue(parser, name, defaultValue); } - private <T> void validateNoDuplicateElement(@NonNull T element, - @NonNull Collection<T> collection, @NonNull String name) { - if (collection.contains(element)) { - throwOrLogMessage("Duplicate " + name + ": " + element); + @Nullable + private <T> Supplier<T> getAttributeMethodValue(@NonNull XmlResourceParser parser, + @NonNull String name, @NonNull Class<T> returnType, @Nullable Supplier<T> fallbackValue, + @NonNull String tagName) { + String value = parser.getAttributeValue(null, name); + if (value == null) { + return null; } - } - - private void throwOrLogMessage(String message) { - if (mValidationEnabled) { - throw new IllegalArgumentException(message); - } else { - Log.wtf(LOG_TAG, message); + int lastDotIndex = value.lastIndexOf('.'); + if (lastDotIndex == -1) { + throwOrLogMessage("Invalid method \"" + value + "\" for \"" + name + "\" on <" + tagName + + ">"); + return fallbackValue; } - } - - private void throwOrLogMessage(String message, Throwable cause) { - if (mValidationEnabled) { - throw new IllegalArgumentException(message, cause); - } else { - Log.wtf(LOG_TAG, message, cause); + String className = applyJarjarTransform(value.substring(0, lastDotIndex)); + String methodName = value.substring(lastDotIndex + 1); + Method method; + try { + Class<?> clazz = Class.forName(className); + method = clazz.getMethod(methodName); + } catch (Exception e) { + throwOrLogMessage("Cannot find method \"" + value + "\" for \"" + name + "\" on <" + + tagName + ">", e); + return fallbackValue; } - } - - private void throwOrLogForUnknownTag(@NonNull XmlResourceParser parser) { - throwOrLogMessage("Unknown tag: " + parser.getName()); - } - - /** - * Validates the permission names with {@code PackageManager} and ensures that all app ops with - * a permission in {@code AppOpsManager} have declared that permission in its role and ensures - * that all preferred activities are listed in the required components. - */ - private void validateResult(@NonNull ArrayMap<String, PermissionSet> permissionSets, - @NonNull ArrayMap<String, Role> roles) { - if (!mValidationEnabled) { - return; + int methodModifiers = method.getModifiers(); + if ((methodModifiers & Modifier.PUBLIC) != Modifier.PUBLIC) { + throwOrLogMessage("Non-public method \"" + value + "\" for \"" + name + "\" on <" + + tagName + ">"); + return fallbackValue; } - - int permissionSetsSize = permissionSets.size(); - for (int permissionSetsIndex = 0; permissionSetsIndex < permissionSetsSize; - permissionSetsIndex++) { - PermissionSet permissionSet = permissionSets.valueAt(permissionSetsIndex); - - List<Permission> permissions = permissionSet.getPermissions(); - int permissionsSize = permissions.size(); - for (int permissionsIndex = 0; permissionsIndex < permissionsSize; permissionsIndex++) { - Permission permission = permissions.get(permissionsIndex); - - validatePermission(permission); - } + if ((methodModifiers & Modifier.STATIC) != Modifier.STATIC) { + throwOrLogMessage("Non-static method \"" + value + "\" for \"" + name + "\" on <" + + tagName + ">"); + return fallbackValue; } - - int rolesSize = roles.size(); - for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) { - Role role = roles.valueAt(rolesIndex); - - if (!role.isAvailableBySdkVersion()) { - continue; - } - - List<RequiredComponent> requiredComponents = role.getRequiredComponents(); - int requiredComponentsSize = requiredComponents.size(); - for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize; - requiredComponentsIndex++) { - RequiredComponent requiredComponent = requiredComponents.get( - requiredComponentsIndex); - - String permission = requiredComponent.getPermission(); - if (permission != null) { - validatePermission(permission); + Class<?> methodReturnType = method.getReturnType(); + if (!primitiveToWrapperClass(returnType).isAssignableFrom( + primitiveToWrapperClass(methodReturnType))) { + throwOrLogMessage("Unexpected return type for method \"" + value + "\" for \"" + + name + "\" on <" + tagName + ">, expected:" + returnType.getName() + + ", found: " + methodReturnType.getName()); + return fallbackValue; + } + return new Supplier<T>() { + @Override + public T get() { + try { + //noinspection unchecked + return (T) method.invoke(null); + } catch (Exception e) { + throwOrLogMessage("Failed to invoke method \"" + value + "\" for \"" + name + + "\" on <" + tagName + ">", e); + return fallbackValue.get(); } } - - List<Permission> permissions = role.getPermissions(); - int permissionsSize = permissions.size(); - for (int i = 0; i < permissionsSize; i++) { - Permission permission = permissions.get(i); - - validatePermission(permission); - } - - List<AppOp> appOps = role.getAppOps(); - int appOpsSize = appOps.size(); - for (int i = 0; i < appOpsSize; i++) { - AppOp appOp = appOps.get(i); - - validateAppOp(appOp); + @Override + public String toString() { + return "Method{name=" + value + "}"; } - - List<Permission> appOpPermissions = role.getAppOpPermissions(); - int appOpPermissionsSize = appOpPermissions.size(); - for (int i = 0; i < appOpPermissionsSize; i++) { - validateAppOpPermission(appOpPermissions.get(i)); - } - - List<PreferredActivity> preferredActivities = role.getPreferredActivities(); - int preferredActivitiesSize = preferredActivities.size(); - for (int preferredActivitiesIndex = 0; - preferredActivitiesIndex < preferredActivitiesSize; - preferredActivitiesIndex++) { - PreferredActivity preferredActivity = preferredActivities.get( - preferredActivitiesIndex); - - if (!role.getRequiredComponents().contains(preferredActivity.getActivity())) { - throw new IllegalArgumentException("<activity> of <preferred-activity> not" - + " required in <required-components>, role: " + role.getName() - + ", preferred activity: " + preferredActivity); - } - } - } + }; } - private void validatePermission(@NonNull Permission permission) { - if (!permission.isAvailableAsUser(Process.myUserHandle(), mContext)) { - return; + // LINT.IfChange(applyJarjarTransform) + /** + * Simulate the jarjar transform that should happen on the class name. + * <p> + * Currently this only handles the {@code Flags} classes for feature flagging. + */ + @NonNull + private String applyJarjarTransform(@NonNull String className) { + if (className.endsWith(".Flags")) { + String jarjarPrefix = Objects.equals(mContext.getPackageName(), "android") + ? "com.android.permission.jarjar." : "com.android.permissioncontroller.jarjar."; + return jarjarPrefix + className; } - validatePermission(permission.getName(), true); + return className; } + // LINT.ThenChange() - private void validatePermission(@NonNull String permission) { - validatePermission(permission, false); + /** + * Return the wrapper class for the given class if it's a primitive type, or the class itself + * otherwise. + */ + @NonNull + private Class<?> primitiveToWrapperClass(@NonNull Class<?> clazz) { + if (clazz.isPrimitive()) { + return sPrimitiveToWrapperClass.get(clazz); + } + return clazz; } - private void validatePermission(@NonNull String permission, boolean enforceIsRuntimeOrRole) { - PackageManager packageManager = mContext.getPackageManager(); - boolean isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - // Skip validation for car permissions which may not be available on all build targets. - if (!isAutomotive && permission.startsWith("android.car")) { - return; + @Nullable + private Supplier<Boolean> mergeFeatureFlags(@Nullable Supplier<Boolean> featureFlag1, + @Nullable Supplier<Boolean> featureFlag2) { + if (featureFlag1 == null) { + return featureFlag2; } - - PermissionInfo permissionInfo; - try { - permissionInfo = packageManager.getPermissionInfo(permission, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("Unknown permission: " + permission, e); + if (featureFlag2 == null) { + return featureFlag1; } + return () -> featureFlag1.get() && featureFlag2.get(); + } - if (enforceIsRuntimeOrRole) { - if (!(permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS - || (permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_ROLE) - == PermissionInfo.PROTECTION_FLAG_ROLE)) { - throw new IllegalArgumentException( - "Permission is not a runtime or role permission: " + permission); - } + private <T> void validateNoDuplicateElement(@NonNull T element, + @NonNull Collection<T> collection, @NonNull String name) { + if (collection.contains(element)) { + throwOrLogMessage("Duplicate " + name + ": " + element); } } - private void validateAppOpPermission(@NonNull Permission appOpPermission) { - if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), mContext)) { - return; + private void throwOrLogMessage(String message) { + if (mThrowOnError) { + throw new IllegalArgumentException(message); + } else { + Log.wtf(LOG_TAG, message); } - validateAppOpPermission(appOpPermission.getName()); } - private void validateAppOpPermission(@NonNull String appOpPermission) { - PackageManager packageManager = mContext.getPackageManager(); - PermissionInfo permissionInfo; - try { - permissionInfo = packageManager.getPermissionInfo(appOpPermission, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("Unknown app op permission: " + appOpPermission, e); - } - if ((permissionInfo.getProtectionFlags() & PermissionInfo.PROTECTION_FLAG_APPOP) - != PermissionInfo.PROTECTION_FLAG_APPOP) { - throw new IllegalArgumentException("Permission is not an app op permission: " - + appOpPermission); + private void throwOrLogMessage(String message, Throwable cause) { + if (mThrowOnError) { + throw new IllegalArgumentException(message, cause); + } else { + Log.wtf(LOG_TAG, message, cause); } } - private void validateAppOp(@NonNull AppOp appOp) { - // This throws IllegalArgumentException if app op is unknown. - String permission = AppOpsManager.opToPermission(appOp.getName()); - if (permission != null) { - PackageManager packageManager = mContext.getPackageManager(); - PermissionInfo permissionInfo; - try { - permissionInfo = packageManager.getPermissionInfo(permission, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } - if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS) { - throw new IllegalArgumentException("App op has an associated runtime permission: " - + appOp.getName()); - } - } + private void throwOrLogForUnknownTag(@NonNull XmlResourceParser parser) { + throwOrLogMessage("Unknown tag: " + parser.getName()); } } diff --git a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java index bc7562c11..d00fd47af 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java +++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.role.controller.service; +import android.annotation.UserIdInt; import android.app.role.RoleControllerService; import android.app.role.RoleManager; import android.content.Context; @@ -34,6 +35,7 @@ import com.android.role.controller.model.Roles; import com.android.role.controller.util.CollectionUtils; import com.android.role.controller.util.LegacyRoleFallbackEnabledUtils; import com.android.role.controller.util.PackageUtils; +import com.android.role.controller.util.RoleFlags; import com.android.role.controller.util.UserUtils; import java.util.ArrayList; @@ -49,11 +51,21 @@ public class RoleControllerServiceImpl extends RoleControllerService { private static final boolean DEBUG = false; + public static volatile SetActiveUserForRoleMethod sSetActiveUserForRoleMethod; private UserHandle mUser; private Context mContext; private RoleManager mUserRoleManager; + /** Method for setting active user from role controller */ + public interface SetActiveUserForRoleMethod { + /** + * Sets user as active for the given role. + * @see RoleManager#setActiveUserForRole(String, UserHandle, int) + */ + void setActiveUserForRole(@NonNull String roleName, @UserIdInt int userId, int flags); + } + public RoleControllerServiceImpl() {} public RoleControllerServiceImpl(@NonNull UserHandle user, @NonNull Context context) { @@ -121,6 +133,19 @@ public class RoleControllerServiceImpl extends RoleControllerService { String roleName = role.getName(); + if (RoleFlags.isProfileGroupExclusivityAvailable() + && role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) { + if (mUserRoleManager.getActiveUserForRole(roleName) == null) { + UserHandle profileParent = UserUtils.getProfileParentOrSelf(mUser, mContext); + if (Objects.equals(mUser, profileParent)) { + Log.i(LOG_TAG, "No active user for role: " + roleName + ", setting " + + "active user to user: " + mUser.getIdentifier()); + sSetActiveUserForRoleMethod.setActiveUserForRole(roleName, + mUser.getIdentifier(), 0); + } + } + } + // For each of the current holders, check if it is still qualified, redo grant if so, or // remove it otherwise. List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName); @@ -232,6 +257,11 @@ public class RoleControllerServiceImpl extends RoleControllerService { boolean added = false; if (role.isExclusive()) { + if (role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) { + sSetActiveUserForRoleMethod.setActiveUserForRole(roleName, mUser.getIdentifier(), + flags); + } + List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName); int currentPackageNamesSize = currentPackageNames.size(); for (int i = 0; i < currentPackageNamesSize; i++) { diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java new file mode 100644 index 000000000..2c5a247b6 --- /dev/null +++ b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.role.controller.util; + +import android.os.Build; + +import androidx.annotation.ChecksSdkIntAtLeast; + +import java.util.Objects; + +/** Util class for getting shared feature flag check logic. */ +public final class RoleFlags { + private RoleFlags() { /* cannot be instantiated */ } + + /** + * Returns whether profile group exclusive roles are available. Profile exclusive roles are + * available on B+ + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.BAKLAVA) + public static boolean isProfileGroupExclusivityAvailable() { + // TODO(b/372743073): change to isAtLeastB once available + return isAtLeastB() && com.android.permission.flags.Flags.crossUserRoleEnabled(); + } + + // TODO(b/372743073): remove once SdkLevel.isAtLeastB available + @ChecksSdkIntAtLeast(api = 36 /* BUILD_VERSION_CODES.Baklava */) + public static boolean isAtLeastB() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA + || Objects.equals(Build.VERSION.CODENAME, "Baklava"); + } +} diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java index 1b6926ef8..f3cb7926a 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java +++ b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java @@ -24,6 +24,7 @@ import android.os.UserHandle; import android.os.UserManager; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; @@ -111,4 +112,24 @@ public final class UserUtils { return context.createContextAsUser(user, 0); } } + + /** + * Returns the parent of a given user, or user if it has no parent (e.g. it is the primary + * user) + */ + @NonNull + public static UserHandle getProfileParentOrSelf(@NonNull UserHandle user, + @NonNull Context context) { + UserHandle profileParent = getProfileParent(user, context); + // If profile parent user is null, then original user is the parent + return profileParent != null ? profileParent : user; + } + + /** Returns the parent of a given user. */ + @Nullable + private static UserHandle getProfileParent(UserHandle user, @NonNull Context context) { + Context userContext = getUserContext(context, user); + UserManager userManager = userContext.getSystemService(UserManager.class); + return userManager.getProfileParent(user); + } } diff --git a/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java b/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java index ec3642d1d..fb3536e1a 100644 --- a/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/DeviceUtils.java @@ -31,4 +31,13 @@ public class DeviceUtils { public static boolean isAuto(Context context) { return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); } + + public static boolean isHandheld() { + Context context = PermissionControllerApplication.get(); + return !isTelevision(context) && !isAuto(context) && !isWear(context); + } + + public static boolean isHandheld(Context context) { + return !isTelevision(context) && !isAuto(context) && !isWear(context); + } } diff --git a/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/DiscretePackageOpsModel.kt b/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/DiscretePackageOpsModel.kt new file mode 100644 index 000000000..a24aff1f9 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/DiscretePackageOpsModel.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.appops.data.model.v31 + +/** + * Data class representing historical package ops read from DiscreteRegistry, These ops are for one + * permission group, as the permission timeline page show one group at a time. + */ +data class DiscretePackageOpsModel( + val packageName: String, + val userId: Int, + val appOpAccesses: List<DiscreteOpModel>, + // some package may support attribution, in that case we want to group ops by label. + var attributionLabel: String? = null, + // variable to indicate whether the permission group is user sensitive or not. + var isUserSensitive: Boolean = false, +) { + /** + * The time stamps (i.e. accessTimeMillis and durationMillis) are with precision rounded to + * minute. + */ + data class DiscreteOpModel( + val opName: String, + val accessTimeMillis: Long, + val durationMillis: Long, + val attributionTag: String? = null, + val proxyPackageName: String? = null, + val proxyUserId: Int? = null, + ) +} diff --git a/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt index 8f3286e36..3057f05e9 100644 --- a/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt +++ b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt @@ -17,23 +17,41 @@ package com.android.permissioncontroller.appops.data.repository.v31 import android.app.AppOpsManager +import android.app.AppOpsManager.AttributedOpEntry +import android.app.AppOpsManager.HISTORY_FLAG_DISCRETE +import android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS +import android.app.AppOpsManager.HistoricalOps +import android.app.AppOpsManager.HistoricalOpsRequest +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.Application -import android.content.pm.PackageManager import android.os.UserHandle +import android.permission.flags.Flags import android.util.Log import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel -import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver +import com.android.permissioncontroller.data.repository.v31.AppOpChangeListener +import com.android.permissioncontroller.data.repository.v31.PackageChangeListener +import com.android.permissioncontroller.data.repository.v31.PermissionChangeListener import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository import com.android.permissioncontroller.permission.utils.PermissionMapping +import java.util.concurrent.TimeUnit import kotlin.concurrent.Volatile +import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.launch /** * This repository encapsulate app op data (i.e. app op usage, app op mode, historical ops etc.) @@ -48,6 +66,17 @@ interface AppOpRepository { */ val packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>> + /** + * Returns a flow of discrete package ops. + * + * @param opNames A list of app op names. + * @param coroutineScope the coroutine scope where we fetch the data asynchronously. + */ + fun getDiscreteOps( + opNames: List<String>, + coroutineScope: CoroutineScope + ): Flow<List<DiscretePackageOpsModel>> + companion object { @Volatile private var instance: AppOpRepository? = null @@ -73,91 +102,112 @@ class AppOpRepositoryImpl( private val appOpNames = getPrivacyDashboardAppOpNames() - override val packageAppOpsUsages by lazy { - callbackFlow { - send(getPackageOps()) - - // Suppress OnOpNotedListener lint error, startWatchingNoted is behind sdk check. - @SuppressWarnings("NewApi") - val callback = - object : - PackageManager.OnPermissionsChangedListener, - PackageBroadcastReceiver.PackageBroadcastListener, - AppOpsManager.OnOpActiveChangedListener, - AppOpsManager.OnOpNotedListener, - AppOpsManager.OnOpChangedListener { - override fun onPermissionsChanged(uid: Int) { - sendUpdate() - } + override fun getDiscreteOps( + opNames: List<String>, + coroutineScope: CoroutineScope + ): Flow<List<DiscretePackageOpsModel>> { + return callbackFlow { + var job: Job? = null + send(getDiscreteOps(opNames)) - override fun onOpChanged(op: String?, packageName: String?) { - sendUpdate() - } - - override fun onPackageUpdate(packageName: String) { - sendUpdate() - } - - override fun onOpActiveChanged( - op: String, - uid: Int, - packageName: String, - active: Boolean - ) { - sendUpdate() - } + fun sendUpdate() { + if (job == null || job?.isActive == false) { + job = coroutineScope.launch { trySend(getDiscreteOps(opNames)) } + } + } - override fun onOpNoted( - op: String, - uid: Int, - packageName: String, - attributionTag: String?, - flags: Int, - result: Int - ) { - sendUpdate() - } + val appOpListener = + AppOpChangeListener(opNames.toSet(), appOpsManager) { sendUpdate() } + val packageListener = PackageChangeListener { sendUpdate() } + val permissionListener = PermissionChangeListener(packageManager) { sendUpdate() } + packageListener.register() + appOpListener.register() + permissionListener.register() + awaitClose { + appOpListener.unregister() + packageListener.unregister() + permissionListener.unregister() + } + } + .flowOn(dispatcher) + } - fun sendUpdate() { - trySend(getPackageOps()) + private suspend fun getDiscreteOps(opNames: List<String>): List<DiscretePackageOpsModel> { + val duration = + if (DeviceUtils.isHandheld()) TimeUnit.DAYS.toMillis(7) else TimeUnit.DAYS.toMillis(1) + val currentTime = System.currentTimeMillis() + val beginTimeMillis = currentTime - duration + val request = + HistoricalOpsRequest.Builder(beginTimeMillis, currentTime) + .setFlags(OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED) + .setOpNames(opNames) + .setHistoryFlags(HISTORY_FLAG_DISCRETE or HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) + .build() + val historicalOps: HistoricalOps = suspendCoroutine { + appOpsManager.getHistoricalOps(request, { it.run() }) { ops: HistoricalOps -> + it.resumeWith(Result.success(ops)) + } + } + val discreteOpsResult = mutableListOf<DiscretePackageOpsModel>() + // Read through nested (uid -> package name -> attribution tag -> op -> discrete events) + // historical ops data structure + for (uidIndex in 0 until historicalOps.uidCount) { + val historicalUidOps = historicalOps.getUidOpsAt(uidIndex) + val userId = UserHandle.getUserHandleForUid(historicalUidOps.uid).identifier + for (packageIndex in 0 until historicalUidOps.packageCount) { + val historicalPackageOps = historicalUidOps.getPackageOpsAt(packageIndex) + val packageName = historicalPackageOps.packageName + val appOpEvents = mutableListOf<DiscreteOpModel>() + for (tagIndex in 0 until historicalPackageOps.attributedOpsCount) { + val attributedHistoricalOps = historicalPackageOps.getAttributedOpsAt(tagIndex) + for (opIndex in 0 until attributedHistoricalOps.opCount) { + val historicalOp = attributedHistoricalOps.getOpAt(opIndex) + for (index in 0 until historicalOp.discreteAccessCount) { + val attributedOpEntry: AttributedOpEntry = + historicalOp.getDiscreteAccessAt(index) + val proxy = attributedOpEntry.getLastProxyInfo(OPS_LAST_ACCESS_FLAGS) + val opEvent = + DiscreteOpModel( + opName = historicalOp.opName, + accessTimeMillis = + attributedOpEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS), + durationMillis = + attributedOpEntry.getLastDuration(OPS_LAST_ACCESS_FLAGS), + attributionTag = attributedHistoricalOps.tag, + proxyPackageName = proxy?.packageName, + proxyUserId = + proxy?.uid?.let { + UserHandle.getUserHandleForUid(it).identifier + }, + ) + appOpEvents.add(opEvent) } } + } + discreteOpsResult.add(DiscretePackageOpsModel(packageName, userId, appOpEvents)) + } + } + return discreteOpsResult + } - packageManager.addOnPermissionsChangeListener(callback) - PackageBroadcastReceiver.addAllCallback(callback) - appOpNames.forEach { opName -> - // TODO(b/262035952): We watch each active op individually as - // startWatchingActive only registers the callback if all ops are valid. - // Fix this behavior so if one op is invalid it doesn't affect the other ops. - try { - appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, callback) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } - - try { - appOpsManager.startWatchingMode(opName, /* all packages */ null, callback) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } + override val packageAppOpsUsages by lazy { + callbackFlow { + send(getPackageOps()) - if (SdkLevel.isAtLeastU()) { - try { - appOpsManager.startWatchingNoted(arrayOf(opName), callback) - } catch (ignored: IllegalArgumentException) { - // Older builds may not support all requested app ops. - } - } + fun sendUpdate() { + trySend(getPackageOps()) } + val appOpListener = AppOpChangeListener(appOpNames, appOpsManager) { sendUpdate() } + val packageListener = PackageChangeListener { sendUpdate() } + val permissionListener = PermissionChangeListener(packageManager) { sendUpdate() } + packageListener.register() + appOpListener.register() + permissionListener.register() awaitClose { - packageManager.removeOnPermissionsChangeListener(callback) - PackageBroadcastReceiver.removeAllCallback(callback) - appOpsManager.stopWatchingActive(callback) - appOpsManager.stopWatchingMode(callback) - if (SdkLevel.isAtLeastU()) { - appOpsManager.stopWatchingNoted(callback) - } + appOpListener.unregister() + packageListener.unregister() + permissionListener.unregister() } } .flowOn(dispatcher) @@ -202,6 +252,9 @@ class AppOpRepositoryImpl( if (SdkLevel.isAtLeastT()) { opNames.add(AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO) } + if (SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled()) { + opNames.add(AppOpsManager.OPSTR_EMERGENCY_LOCATION) + } return opNames } @@ -209,8 +262,6 @@ class AppOpRepositoryImpl( private const val LOG_TAG = "AppOpUsageRepository" private const val OPS_LAST_ACCESS_FLAGS = - AppOpsManager.OP_FLAG_SELF or - AppOpsManager.OP_FLAG_TRUSTED_PROXIED or - AppOpsManager.OP_FLAG_TRUSTED_PROXY + OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED or OP_FLAG_TRUSTED_PROXY } } diff --git a/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/package-info.java b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/package-info.java new file mode 100644 index 000000000..c51eab598 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.S) +package com.android.permissioncontroller.appops.data.repository.v31; diff --git a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt index 0ee0e0d01..d5ef1ebc8 100644 --- a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt +++ b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt @@ -56,7 +56,7 @@ class DrivingDecisionReminderService : Service() { data class PermissionReminder( val packageName: String, val permissionGroup: String, - val user: UserHandle + val user: UserHandle, ) private var scheduled = false @@ -85,7 +85,7 @@ class DrivingDecisionReminderService : Service() { context: Context, packageName: String, permissionGroup: String, - user: UserHandle + user: UserHandle, ): Intent { val intent = Intent(context, DrivingDecisionReminderService::class.java) intent.putExtra(EXTRA_PACKAGE_NAME, packageName) @@ -101,7 +101,7 @@ class DrivingDecisionReminderService : Service() { fun startServiceIfCurrentlyRestricted( context: Context, packageName: String, - permGroupName: String + permGroupName: String, ) { Car.createCar(context, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) { car: Car, @@ -120,20 +120,20 @@ class DrivingDecisionReminderService : Service() { context, packageName, permGroupName, - Process.myUserHandle() + Process.myUserHandle(), ) ) } } else { DumpableLog.e( LOG_TAG, - "Reminder service not created because CarUxRestrictions is null" + "Reminder service not created because CarUxRestrictions is null", ) } } else { DumpableLog.e( LOG_TAG, - "Reminder service not created because CarUxRestrictionsManager is null" + "Reminder service not created because CarUxRestrictionsManager is null", ) } } @@ -145,7 +145,7 @@ class DrivingDecisionReminderService : Service() { val notificationManager = context.getSystemService(NotificationManager::class.java)!! notificationManager.cancel( DrivingDecisionReminderService::class.java.simpleName, - Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID + Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID, ) } } @@ -183,7 +183,7 @@ class DrivingDecisionReminderService : Service() { } else { DumpableLog.w( LOG_TAG, - "Car service disconnected, no notification will be scheduled" + "Car service disconnected, no notification will be scheduled", ) stopSelf() } @@ -198,7 +198,7 @@ class DrivingDecisionReminderService : Service() { if (!restrictions.isRequiresDistractionOptimization) { DumpableLog.d( LOG_TAG, - "UX restrictions no longer required - showing reminder notification" + "UX restrictions no longer required - showing reminder notification", ) showRecentGrantDecisionsPostDriveNotification() stopSelf() @@ -230,14 +230,14 @@ class DrivingDecisionReminderService : Service() { NotificationChannel( Constants.PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders), - NotificationManager.IMPORTANCE_HIGH + NotificationManager.IMPORTANCE_HIGH, ) notificationManager.createNotificationChannel(permissionReminderChannel) notificationManager.notify( DrivingDecisionReminderService::class.java.simpleName, Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID, - createNotification(createNotificationTitle(), createNotificationContent()) + createNotification(createNotificationTitle(), createNotificationContent()), ) logNotificationPresented() @@ -266,26 +266,26 @@ class DrivingDecisionReminderService : Service() { applicationContext, R.string.post_drive_permission_decision_reminder_summary_multi_apps, (packageLabels.size - 1), - packageLabelsDistinct[0] + packageLabelsDistinct[0], ) } else if (permissionGroupNamesDistinct.size == 2) { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_2_permissions, packageLabelsDistinct[0], permissionGroupNamesDistinct[0], - permissionGroupNamesDistinct[1] + permissionGroupNamesDistinct[1], ) } else if (permissionGroupNamesDistinct.size > 2) { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_multi_permission, permissionGroupNamesDistinct.size, - packageLabelsDistinct[0] + packageLabelsDistinct[0], ) } else { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_1_permission, packageLabelsDistinct[0], - permissionGroupNamesDistinct[0] + permissionGroupNamesDistinct[0], ) } } @@ -302,9 +302,9 @@ class DrivingDecisionReminderService : Service() { putExtra(Constants.EXTRA_SESSION_ID, sessionId) putExtra( AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE, - AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION + AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION, ) - flags = Intent.FLAG_ACTIVITY_NEW_TASK + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent = PendingIntent.getActivity( @@ -313,14 +313,14 @@ class DrivingDecisionReminderService : Service() { clickIntent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) val settingsIcon = KotlinUtils.getSettingsIcon( application, permissionReminders.first().user, - applicationContext.packageManager + applicationContext.packageManager, ) val b = @@ -342,7 +342,7 @@ class DrivingDecisionReminderService : Service() { Notification.Action.Builder( /* icon= */ null, getString(R.string.go_to_settings), - pendingIntent + pendingIntent, ) .build() ) @@ -358,7 +358,7 @@ class DrivingDecisionReminderService : Service() { PermissionControllerStatsLog.write( PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED, sessionId, - PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED + PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/data/repository/v31/RepositoryListeners.kt b/PermissionController/src/com/android/permissioncontroller/data/repository/v31/RepositoryListeners.kt new file mode 100644 index 000000000..7c61a1f82 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/data/repository/v31/RepositoryListeners.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.data.repository.v31 + +import android.app.AppOpsManager +import android.content.pm.PackageManager +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver + +class PermissionChangeListener( + private val packageManager: PackageManager, + private val callback: () -> Unit +) : PackageManager.OnPermissionsChangedListener { + fun register() { + packageManager.addOnPermissionsChangeListener(this) + } + + fun unregister() { + packageManager.removeOnPermissionsChangeListener(this) + } + + override fun onPermissionsChanged(uid: Int) { + callback() + } +} + +class PackageChangeListener(private val callback: () -> Unit) : + PackageBroadcastReceiver.PackageBroadcastListener { + + fun register() { + PackageBroadcastReceiver.addAllCallback(this) + } + + fun unregister() { + PackageBroadcastReceiver.removeAllCallback(this) + } + + override fun onPackageUpdate(packageName: String) { + callback() + } +} + +// Suppress OnOpNotedListener, it was introduced in Q but became System API in U. +@SuppressWarnings("NewApi") +class AppOpChangeListener( + private val opNames: Set<String>, + private val appOpsManager: AppOpsManager, + private val callback: () -> Unit +) : + AppOpsManager.OnOpChangedListener, + AppOpsManager.OnOpNotedListener, + AppOpsManager.OnOpActiveChangedListener { + + fun register() { + opNames.forEach { opName -> + // TODO(b/262035952): We watch each active op individually as + // startWatchingActive only registers the callback if all ops are valid. + // Fix this behavior so if one op is invalid it doesn't affect the other ops. + try { + appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, this) + } catch (ignored: IllegalArgumentException) { + // Older builds may not support all requested app ops. + } + + try { + appOpsManager.startWatchingMode(opName, /* all packages */ null, this) + } catch (ignored: IllegalArgumentException) { + // Older builds may not support all requested app ops. + } + + if (SdkLevel.isAtLeastU()) { + try { + appOpsManager.startWatchingNoted(arrayOf(opName), this) + } catch (ignored: IllegalArgumentException) { + // Older builds may not support all requested app ops. + } + } + } + } + + fun unregister() { + appOpsManager.stopWatchingActive(this) + appOpsManager.stopWatchingMode(this) + if (SdkLevel.isAtLeastU()) { + appOpsManager.stopWatchingNoted(this) + } + } + + override fun onOpChanged(op: String?, packageName: String?) { + callback() + } + + override fun onOpNoted( + op: String, + uid: Int, + packageName: String, + attributionTag: String?, + flags: Int, + result: Int + ) { + callback() + } + + override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) { + callback() + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt index bc6f774ad..e6cf094e3 100644 --- a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt +++ b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt @@ -18,6 +18,7 @@ package com.android.permissioncontroller.ecm import android.annotation.SuppressLint import android.app.AlertDialog +import android.app.AppOpsManager import android.app.Dialog import android.app.ecm.EnhancedConfirmationManager import android.content.Context @@ -65,6 +66,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { finish() return } + if (savedInstanceState != null) { wasClearRestrictionAllowed = savedInstanceState.getBoolean(KEY_WAS_CLEAR_RESTRICTION_ALLOWED) @@ -79,11 +81,19 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { require(uid != Process.INVALID_UID) { "EXTRA_UID cannot be null or invalid" } require(!packageName.isNullOrEmpty()) { "EXTRA_PACKAGE_NAME cannot be null or empty" } require(!settingIdentifier.isNullOrEmpty()) { "EXTRA_SUBJECT cannot be null or empty" } - wasClearRestrictionAllowed = setClearRestrictionAllowed(packageName, UserHandle.getUserHandleForUid(uid)) val setting = Setting.fromIdentifier(this, settingIdentifier, isEcmInApp) + if ( + SettingType.fromIdentifier(this, settingIdentifier, isEcmInApp) == + SettingType.BLOCKED_DUE_TO_PHONE_STATE && + !Flags.unknownCallPackageInstallBlockingEnabled() + ) { + finish() + return + } + if (DeviceUtils.isWear(this)) { WearEnhancedConfirmationDialogFragment.newInstance(setting.title, setting.message) .show(supportFragmentManager, WearEnhancedConfirmationDialogFragment.TAG) @@ -116,7 +126,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { fun fromIdentifier( context: Context, settingIdentifier: String, - isEcmInApp: Boolean + isEcmInApp: Boolean, ): Setting { val settingType = SettingType.fromIdentifier(context, settingIdentifier, isEcmInApp) val label = @@ -124,7 +134,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { SettingType.PLATFORM_PERMISSION -> KotlinUtils.getPermGroupLabel( context, - PermissionMapping.getGroupOfPlatformPermission(settingIdentifier)!! + PermissionMapping.getGroupOfPlatformPermission(settingIdentifier)!!, ) SettingType.PLATFORM_PERMISSION_GROUP -> KotlinUtils.getPermGroupLabel(context, settingIdentifier) @@ -132,15 +142,22 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { context.getString( Roles.get(context)[settingIdentifier]!!.shortLabelResource ) + SettingType.BLOCKED_DUE_TO_PHONE_STATE, SettingType.OTHER -> null } - val url = - context.getString(R.string.help_url_action_disabled_by_restricted_settings) - return Setting( - title = settingType.titleRes?.let { context.getString(it, label) }, + var title: String? + var message: CharSequence? + if (settingType == SettingType.BLOCKED_DUE_TO_PHONE_STATE) { + title = settingType.titleRes?.let { context.getString(it) } + message = settingType.messageRes?.let { context.getString(it) } + } else { + val url = + context.getString(R.string.help_url_action_disabled_by_restricted_settings) + title = (settingType.titleRes?.let { context.getString(it, label) }) message = settingType.messageRes?.let { Html.fromHtml(context.getString(it, url), 0) } - ) + } + return Setting(title, message) } } } @@ -148,29 +165,35 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { private enum class SettingType(val titleRes: Int?, val messageRes: Int?) { PLATFORM_PERMISSION( R.string.enhanced_confirmation_dialog_title_permission, - R.string.enhanced_confirmation_dialog_desc_permission + R.string.enhanced_confirmation_dialog_desc_permission, ), PLATFORM_PERMISSION_GROUP( R.string.enhanced_confirmation_dialog_title_permission, - R.string.enhanced_confirmation_dialog_desc_permission + R.string.enhanced_confirmation_dialog_desc_permission, ), ROLE( R.string.enhanced_confirmation_dialog_title_role, - R.string.enhanced_confirmation_dialog_desc_role + R.string.enhanced_confirmation_dialog_desc_role, ), OTHER( R.string.enhanced_confirmation_dialog_title_settings_default, - R.string.enhanced_confirmation_dialog_desc_settings_default + R.string.enhanced_confirmation_dialog_desc_settings_default, + ), + BLOCKED_DUE_TO_PHONE_STATE( + R.string.enhanced_confirmation_phone_state_dialog_title, + R.string.enhanced_confirmation_phone_state_dialog_desc, ); companion object { fun fromIdentifier( context: Context, settingIdentifier: String, - isEcmInApp: Boolean + isEcmInApp: Boolean, ): SettingType { - if (!isEcmInApp) return SettingType.OTHER return when { + settingIdentifier == AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES -> + BLOCKED_DUE_TO_PHONE_STATE + !isEcmInApp -> OTHER PermissionMapping.isRuntimePlatformPermission(settingIdentifier) && PermissionMapping.getGroupOfPlatformPermission(settingIdentifier) != null -> PLATFORM_PERMISSION @@ -178,7 +201,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { PLATFORM_PERMISSION_GROUP settingIdentifier.startsWith("android.app.role.") && Roles.get(context).containsKey(settingIdentifier) -> ROLE - else -> SettingType.OTHER + else -> OTHER } } } @@ -188,7 +211,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { this.dialogResult = dialogResult setResult( RESULT_OK, - Intent().apply { putExtra(Intent.EXTRA_RETURN_RESULT, dialogResult.statsLogValue) } + Intent().apply { putExtra(Intent.EXTRA_RETURN_RESULT, dialogResult.statsLogValue) }, ) finish() } @@ -200,7 +223,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID), settingIdentifier = intent.getStringExtra(Intent.EXTRA_SUBJECT)!!, firstShowForApp = !wasClearRestrictionAllowed, - dialogResult = dialogResult + dialogResult = dialogResult, ) } } @@ -249,7 +272,7 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { private fun createDialogView( context: Context, title: String?, - message: CharSequence? + message: CharSequence?, ): View = LayoutInflater.from(context) .inflate(R.layout.enhanced_confirmation_dialog, null) diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt index d0e97f365..8b11036e8 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt @@ -42,6 +42,7 @@ import android.app.usage.UsageStatsManager.INTERVAL_DAILY import android.app.usage.UsageStatsManager.INTERVAL_MONTHLY import android.content.BroadcastReceiver import android.content.ComponentName +import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_NEW_TASK @@ -49,6 +50,8 @@ import android.content.Intent.FLAG_RECEIVER_FOREGROUND import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.pm.PackageManager.PERMISSION_GRANTED +import android.database.ContentObserver +import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Process @@ -59,6 +62,7 @@ import android.printservice.PrintService import android.provider.DeviceConfig import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION import android.provider.Settings +import android.provider.Settings.Secure.USER_SETUP_COMPLETE import android.safetycenter.SafetyCenterManager import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceData @@ -69,6 +73,7 @@ import android.service.dreams.DreamService import android.service.notification.NotificationListenerService import android.service.voice.VoiceInteractionService import android.service.wallpaper.WallpaperService +import android.telecom.TelecomManager import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS import android.util.Log @@ -105,13 +110,13 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.StringUtils import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.forEachInParallel -import java.util.Date -import java.util.Random -import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import java.util.Date +import java.util.Random +import java.util.concurrent.TimeUnit private const val LOG_TAG = "HibernationPolicy" const val DEBUG_OVERRIDE_THRESHOLDS = false @@ -144,7 +149,7 @@ private fun getCheckFrequencyMs() = // Intentionally kept value of the key same as before because we want to continue reading value of // this shared preference stored by previous versions of PermissionController const val PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING = "first_boot_time" -const val PREF_KEY_BOOT_TIME_SNAPSHOT = "ah_boot_time_snapshot" +const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "ah_boot_time_snapshot" const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "ah_elapsed_realtime_snapshot" private const val PREFS_FILE_NAME = "unused_apps_prefs" @@ -274,7 +279,29 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action + val contentResolver = context.contentResolver if (action == Intent.ACTION_BOOT_COMPLETED || action == ACTION_SET_UP_HIBERNATION) { + if (isUserSetupComplete(contentResolver)) { + maybeInitStartTimeUnusedAppTracking(context.sharedPreferences) + } else { + // User set-up is not complete. Delay this until it is. + // This is best-effort as the process may be killed before set-up completes + // which prevents this callback from being called. + // In that case, unused app tracking will instead start when the job first runs + contentResolver.registerContentObserver( + Settings.Secure.getUriFor(USER_SETUP_COMPLETE), + /* notifyForDescendants= */ false, + object : ContentObserver(/* handler= */ null) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + if (uri == Settings.Secure.getUriFor(USER_SETUP_COMPLETE) + && isUserSetupComplete(contentResolver)) { + contentResolver.unregisterContentObserver(this) + maybeInitStartTimeUnusedAppTracking(context.sharedPreferences) + } + } + } + ) + } if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i( LOG_TAG, @@ -284,7 +311,7 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { ) } - initStartTimeOfUnusedAppTracking(context.sharedPreferences) + setSystemTimeSnapshots(context.sharedPreferences) // If this user is a profile, then its hibernation/auto-revoke will be handled by the // primary user @@ -329,7 +356,7 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { } } if (action == Intent.ACTION_TIME_CHANGED || action == Intent.ACTION_TIMEZONE_CHANGED) { - adjustStartTimeOfUnusedAppTracking(context.sharedPreferences) + adjustSnapshotTimes(context.sharedPreferences) } } @@ -632,6 +659,36 @@ suspend fun isPackageHibernationExemptBySystem( return true } + // Note that it's fine to check permissions instead of app ops as all these permissions were + // introduced before auto-revoke / hibernation in R. + val hasCallRelatedPermissions = + context.checkPermission(Manifest.permission.MANAGE_OWN_CALLS, -1 /* pid */, pkg.uid) == + PERMISSION_GRANTED + && context.checkPermission(Manifest.permission.RECORD_AUDIO, -1 /* pid */, pkg.uid) == + PERMISSION_GRANTED + && context.checkPermission(Manifest.permission.WRITE_CALL_LOG, -1 /* pid */, pkg.uid) == + PERMISSION_GRANTED + if (hasCallRelatedPermissions) { + val phoneAccounts = context.getSystemService(TelecomManager::class.java)!! + .selfManagedPhoneAccounts + var hasRegisteredPhoneAccount = false + for (phoneAccount in phoneAccounts) { + if (pkg.packageName == phoneAccount.componentName.packageName) { + hasRegisteredPhoneAccount = true + break + } + } + if (hasRegisteredPhoneAccount) { + if (DEBUG_HIBERNATION_POLICY) { + DumpableLog.i( + LOG_TAG, + "Exempted ${pkg.packageName} - caller app" + ) + } + return true + } + } + if (SdkLevel.isAtLeastS()) { val hasInstallOrUpdatePermissions = context.checkPermission(Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) == @@ -764,7 +821,7 @@ internal class SystemTime { private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime { val systemTime = SystemTime() val systemTimeSnapshot = - sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) + sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) if (systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) { DumpableLog.e(LOG_TAG, "PREF_KEY_BOOT_TIME_SNAPSHOT is not initialized") return systemTime @@ -803,7 +860,7 @@ fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long val diffSystemTime = getSystemTime(sharedPreferences).diffSystemTime // If the value stored is older than a day adjust start time. if (diffSystemTime > ONE_DAY_MS) { - adjustStartTimeOfUnusedAppTracking(sharedPreferences) + adjustSnapshotTimes(sharedPreferences) } return sharedPreferences.getLong( PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, @@ -811,7 +868,10 @@ fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long ) } -private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) { +/** + * Init the start time of unused apps tracking if it has not been initialized before + */ +private fun maybeInitStartTimeUnusedAppTracking(sharedPreferences: SharedPreferences) { val systemTimeSnapshot = System.currentTimeMillis() if ( sharedPreferences.getLong( @@ -824,33 +884,43 @@ private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreference .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot) .apply() } +} + +private fun setSystemTimeSnapshots(sharedPreferences: SharedPreferences) { + val systemTimeSnapshot = System.currentTimeMillis() val realtimeSnapshot = SystemClock.elapsedRealtime() sharedPreferences .edit() - .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot) + .putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTimeSnapshot) .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot) .apply() } -private fun adjustStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) { +private fun adjustSnapshotTimes(sharedPreferences: SharedPreferences) { val systemTime = getSystemTime(sharedPreferences) + val editor = sharedPreferences + .edit() + .putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTime.actualSystemTime) + .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime) + val startTimeOfUnusedAppTracking = sharedPreferences.getLong( PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, SNAPSHOT_UNINITIALIZED ) if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) { - DumpableLog.e(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized") - return + DumpableLog.w(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized") + } else { + val adjustedStartTimeOfUnusedAppTracking = + startTimeOfUnusedAppTracking + systemTime.diffSystemTime + editor.putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + adjustedStartTimeOfUnusedAppTracking) } - val adjustedStartTimeOfUnusedAppTracking = - startTimeOfUnusedAppTracking + systemTime.diffSystemTime - sharedPreferences - .edit() - .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, adjustedStartTimeOfUnusedAppTracking) - .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTime.actualSystemTime) - .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime) - .apply() + editor.apply() +} + +private fun isUserSetupComplete(contentResolver: ContentResolver): Boolean { + return Settings.Secure.getInt(contentResolver, USER_SETUP_COMPLETE, 0) != 0 } /** Make intent to go to unused apps page. */ @@ -901,6 +971,16 @@ class HibernationJobService : JobService() { DumpableLog.i(LOG_TAG, "onStartJob") } + if (!isUserSetupComplete(contentResolver)) { + if (DEBUG_HIBERNATION_POLICY) { + DumpableLog.i(LOG_TAG, "Skipping hibernation job because set-up is not complete") + } + jobFinished(params, /* wantsReschedule= */ true) + return true + } + + maybeInitStartTimeUnusedAppTracking(sharedPreferences) + if (SKIP_NEXT_RUN) { SKIP_NEXT_RUN = false if (DEBUG_HIBERNATION_POLICY) { diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING index 69a8f74be..038b2f992 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING @@ -11,5 +11,15 @@ } ] } + ], + "postsubmit": [ + { + "name": "PermissionControllerMockingTests", + "options": [ + { + "include-filter": "com.android.permissioncontroller.tests.mocking.hibernation" + } + ] + } ] } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING index 7ec419864..a94f84f60 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING @@ -1,30 +1,7 @@ { "presubmit": [ { - "name": "CtsPermissionTestCases", - "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": "androidx.test.filters.FlakyTest" - } - ] + "name": "CtsPermissionTestCases_PermissionController" }, { "name": "CtsHibernationTestCases", diff --git a/PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java new file mode 100644 index 000000000..05d69e998 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppOpsManagerCompat.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.compat; + +import android.annotation.SuppressLint; +import android.app.AppOpsManager; +import android.permission.flags.Flags; + +import androidx.annotation.NonNull; + +/** + * Helper AppOpsManager compat class + */ +public class AppOpsManagerCompat { + + private AppOpsManagerCompat() {} + + /** + * For platform version <= V, call the deprecated unsafeCheckOpRawNoThrow. For newer platforms, + * call the new API checkOpRawNoThrow. + * + * @return the raw mode of the op + */ + // TODO: b/379749734 + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + public static int checkOpRawNoThrow(@NonNull AppOpsManager appOpsManager, @NonNull String op, + int uid, @NonNull String packageName) { + if (Flags.checkOpOverloadApiEnabled()) { + return appOpsManager.checkOpRawNoThrow(op, uid, packageName, null); + } else { + return appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName); + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java new file mode 100644 index 000000000..de7404ead --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.compat; + +import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; +import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceFragmentCompat; + +import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.ui.handheld.max35.LegacyAppPermissionFragment; +import com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFragment; + +/** Helper methods for AppPermissionFragment across SDKs for compatibility. */ +public class AppPermissionFragmentCompat { + public static final String GRANT_CATEGORY = "grant_category"; + public static final String PERSISTENT_DEVICE_ID = "persistent_device_id"; + + /** + * Create an instance of this fragment + */ + @NonNull + public static PreferenceFragmentCompat createFragment(@NonNull Context context) { + if (SdkLevel.isAtLeastV() && (Flags.appPermissionFragmentUsesPreferences() + || context.getResources().getBoolean( + R.bool.config_usePreferenceForAppPermissionSettings))) { + return new AppPermissionFragment(); + } else { + return new LegacyAppPermissionFragment(); + } + } + + /** + * Create a bundle with the arguments needed by this fragment + * + * @param packageName The name of the package + * @param permName The name of the permission whose group this fragment is for (optional) + * @param groupName The name of the permission group (required if permName not specified) + * @param userHandle The user of the app permission group + * @param caller The name of the fragment we called from + * @param sessionId The current session ID + * @param grantCategory The grant status of this app permission group. Used to initially set + * the button state + * @param persistentDeviceId A persistent device ID (optional) + * @return A bundle with all of the args placed + */ + public static Bundle createArgs(@NonNull String packageName, + @Nullable String permName, @Nullable String groupName, + @NonNull UserHandle userHandle, @Nullable String caller, long sessionId, + @Nullable String grantCategory, @Nullable String persistentDeviceId) { + Bundle arguments = new Bundle(); + arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); + if (groupName == null) { + arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName); + } else { + arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName); + } + arguments.putParcelable(Intent.EXTRA_USER, userHandle); + arguments.putString(EXTRA_CALLER_NAME, caller); + arguments.putLong(EXTRA_SESSION_ID, sessionId); + arguments.putString(GRANT_CATEGORY, grantCategory); + arguments.putString(PERSISTENT_DEVICE_ID, persistentDeviceId); + return arguments; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt index 1e44f16bd..3202f5b69 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt @@ -19,6 +19,7 @@ package com.android.permissioncontroller.permission.data import android.app.AppOpsManager import android.app.Application import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.compat.AppOpsManagerCompat /** * A LiveData which represents the appop state @@ -36,13 +37,13 @@ private constructor( private val app: Application, private val packageName: String, private val op: String, - private val uid: Int + private val uid: Int, ) : SmartUpdateMediatorLiveData<Int>() { private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! override fun onUpdate() { - value = appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName) + value = AppOpsManagerCompat.checkOpRawNoThrow(appOpsManager, op, uid, packageName) } override fun onActive() { @@ -62,7 +63,7 @@ private constructor( PermissionControllerApplication.get(), key.first, key.second, - key.third + key.third, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt index b17098a13..394cb3eb7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt @@ -233,8 +233,8 @@ private constructor( * user */ private fun isUserSet(permissionState: Map<String, PermState>): Boolean { - val flagMask = - PackageManager.FLAG_PERMISSION_USER_SET or PackageManager.FLAG_PERMISSION_USER_FIXED + val flagMask = PackageManager.FLAG_PERMISSION_USER_SET or + PackageManager.FLAG_PERMISSION_USER_FIXED or PackageManager.FLAG_PERMISSION_ONE_TIME return permissionState.any { (it.value.permFlags and flagMask) != 0 } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt index 4a2d3b68a..2b6d9728e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt @@ -28,6 +28,7 @@ import android.app.Application import android.os.Build import android.os.UserHandle import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.compat.AppOpsManagerCompat import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo import kotlinx.coroutines.Job @@ -46,7 +47,7 @@ object FullStoragePermissionAppsLiveData : val packageName: String, val user: UserHandle, val isLegacy: Boolean, - val isGranted: Boolean + val isGranted: Boolean, ) init { @@ -88,7 +89,7 @@ object FullStoragePermissionAppsLiveData : fun getFullStorageStateForPackage( appOpsManager: AppOpsManager, packageInfo: LightPackageInfo, - userHandle: UserHandle? = null + userHandle: UserHandle? = null, ): FullStoragePackageState? { val sdk = packageInfo.targetSdkVersion val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid) @@ -97,29 +98,31 @@ object FullStoragePermissionAppsLiveData : packageInfo.packageName, user, isLegacy = true, - isGranted = true + isGranted = true, ) } else if ( sdk <= Build.VERSION_CODES.Q && - appOpsManager.unsafeCheckOpNoThrow( + AppOpsManagerCompat.checkOpRawNoThrow( + appOpsManager, OPSTR_LEGACY_STORAGE, packageInfo.uid, - packageInfo.packageName + packageInfo.packageName, ) == MODE_ALLOWED ) { return FullStoragePackageState( packageInfo.packageName, user, isLegacy = true, - isGranted = true + isGranted = true, ) } if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) { val mode = - appOpsManager.unsafeCheckOpNoThrow( + AppOpsManagerCompat.checkOpRawNoThrow( + appOpsManager, OPSTR_MANAGE_EXTERNAL_STORAGE, packageInfo.uid, - packageInfo.packageName + packageInfo.packageName, ) val granted = mode == MODE_ALLOWED || @@ -130,7 +133,7 @@ object FullStoragePermissionAppsLiveData : packageInfo.packageName, user, isLegacy = false, - isGranted = granted + isGranted = granted, ) } return null diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt index 091c45b92..68aa55e14 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt @@ -67,7 +67,7 @@ private constructor(private val app: Application, private val permissionName: St val newValue = try { - LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0)) + LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0), null) } catch (e: PackageManager.NameNotFoundException) { Log.w(LOG_TAG, "Permission \"$permissionName\" not found") invalidateSingle(permissionName) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt index d44fea233..e923746d7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.data import android.app.Application +import android.content.pm.ApplicationInfo import android.content.pm.PackageItemInfo import android.content.pm.PackageManager import android.content.pm.PermissionGroupInfo @@ -64,7 +65,6 @@ private constructor(private val app: Application, private val groupName: String) */ override fun onUpdate() { val permissionInfos = mutableMapOf<String, LightPermInfo>() - groupInfo = Utils.getGroupInfo(groupName, context) ?: run { @@ -73,28 +73,25 @@ private constructor(private val app: Application, private val groupName: String) value = null return } - + val permInfos = mutableListOf<PermissionInfo>() when (groupInfo) { is PermissionGroupInfo -> { - val permInfos = - try { + try { + permInfos.addAll( Utils.getInstalledRuntimePermissionInfosForGroup( context.packageManager, groupName ) - } catch (e: PackageManager.NameNotFoundException) { - Log.e(LOG_TAG, "Invalid permission group $groupName") - invalidateSingle(groupName) - value = null - return - } - - for (permInfo in permInfos) { - permissionInfos[permInfo.name] = LightPermInfo(permInfo) + ) + } catch (e: PackageManager.NameNotFoundException) { + Log.e(LOG_TAG, "Invalid permission group $groupName") + invalidateSingle(groupName) + value = null + return } } is PermissionInfo -> { - permissionInfos[groupInfo.name] = LightPermInfo(groupInfo as PermissionInfo) + permInfos.add(groupInfo as PermissionInfo) } else -> { value = null @@ -102,19 +99,25 @@ private constructor(private val app: Application, private val groupName: String) } } - val permGroup = PermGroup(LightPermGroupInfo(groupInfo), permissionInfos) - - value = permGroup - - val packageNames = - permissionInfos.values.map { permInfo -> permInfo.packageName }.toMutableSet() + val packageNames = permInfos.map { permInfo -> permInfo.packageName }.toMutableSet() packageNames.add(groupInfo.packageName) - // TODO ntmyren: What if the package isn't installed for the system user? val getLiveData = { packageName: String -> LightPackageInfoLiveData[packageName, UserHandle.SYSTEM] } setSourcesToDifference(packageNames, packageLiveDatas, getLiveData) + if (!packageLiveDatas.all { it.value.isInitialized }) { + return + } + for (permInfo in permInfos) { + val lightPackageInfo = packageLiveDatas[permInfo.packageName]?.value + val isSystem = + lightPackageInfo?.let { it.appFlags and ApplicationInfo.FLAG_SYSTEM != 0 } + permissionInfos[permInfo.name] = LightPermInfo(permInfo, isSystem) + } + + val permGroup = PermGroup(LightPermGroupInfo(groupInfo), permissionInfos) + value = permGroup } override fun onInactive() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt index 4b2364cc5..fef152c4e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt @@ -29,3 +29,10 @@ data class PermissionGroupUsageModel( */ val isUserSensitive: Boolean, ) + +sealed class PermissionGroupUsageModelWrapper { + data object Loading : PermissionGroupUsageModelWrapper() + + data class Success(val permissionUsageModels: List<PermissionGroupUsageModel>) : + PermissionGroupUsageModelWrapper() +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionTimelineUsageModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionTimelineUsageModel.kt new file mode 100644 index 000000000..04233299e --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionTimelineUsageModel.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.domain.model.v31 + +/** + * This model class provides data for creating one row/preference on the permission timeline page. + * The time stamps are with precision rounded to minute. + */ +data class PermissionTimelineUsageModel( + val packageName: String, + val userId: Int, + val opNames: Set<String>, + val accessStartMillis: Long, + val accessEndMillis: Long, + val durationMillis: Long, + val isUserSensitive: Boolean = false, + val attributionLabel: String? = null, + val attributionTags: Set<String>? = null, + val proxyPackageName: String? = null, + val proxyUserId: Int? = null, +) + +sealed class PermissionTimelineUsageModelWrapper { + data object Loading : PermissionTimelineUsageModelWrapper() + + data class Success(val timelineUsageModels: List<PermissionTimelineUsageModel>) : + PermissionTimelineUsageModelWrapper() +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt new file mode 100644 index 000000000..5ba649fd3 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.domain.usecase.v31 + +import android.Manifest +import android.app.AppOpsManager +import android.os.UserHandle +import android.permission.flags.Flags +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel +import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository +import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.CLUSTER_SPACING_MINUTES +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.ONE_MINUTE_MS +import com.android.permissioncontroller.permission.utils.LocationUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.role.data.repository.v31.RoleRepository +import com.android.permissioncontroller.user.data.repository.v31.UserRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * This use case reads discrete app ops data and transform it to show the private data access by + * apps on permission timeline page. + */ +class GetPermissionGroupUsageDetailsUseCase( + private val permissionGroup: String, + private val packageRepository: PackageRepository, + private val permissionRepository: PermissionRepository, + private val appOpRepository: AppOpRepository, + private val roleRepository: RoleRepository, + private val userRepository: UserRepository, + // Allow tests to inject as on T- READ_DEVICE_CONFIG permission check is enforced. + private val attributionLabelFix: Boolean = + com.android.permission.flags.Flags.permissionTimelineAttributionLabelFix(), +) { + operator fun invoke(coroutineScope: CoroutineScope): Flow<PermissionTimelineUsageModelWrapper> { + val opNames = requireNotNull(permissionGroupToOpNames[permissionGroup]) + return appOpRepository.getDiscreteOps(opNames, coroutineScope).map { pkgOps -> + val currentUsers = + userRepository + .getUserProfilesIncludingCurrentUser() + .filterUsersToShowInQuietMode(userRepository) + val exemptedPackages = roleRepository.getExemptedPackages() + + val filteredPackageOps: List<DiscretePackageOpsModel> = + pkgOps + .filter { packageOps -> + packageOps.userId in currentUsers && + packageOps.packageName !in exemptedPackages + } + .filterPackagesNotRequestingPermission(permissionGroup) + .map { packageOps -> + packageOps.isUserSensitive = + isPermissionGroupUserSensitive( + packageOps.packageName, + permissionGroup, + packageOps.userId, + permissionRepository, + packageRepository, + ) + packageOps + } + val attributedPackageOps: List<DiscretePackageOpsModel> = + filteredPackageOps.groupByAttributionLabelIfNeeded() + val clusteredPackageOps: List<DiscretePackageOpsModel> = + attributedPackageOps.createAccessClusters() + val permissionTimelineUsageModels: List<PermissionTimelineUsageModel> = + clusteredPackageOps.buildPermissionTimelineUsage() + PermissionTimelineUsageModelWrapper.Success(permissionTimelineUsageModels) + } + } + + // show attribution on T+ for location provider only.. + private fun shouldShowAttributionLabel(packageName: String): Boolean { + return if (attributionLabelFix) { + SdkLevel.isAtLeastT() && + LocationUtils.isLocationProvider(PermissionControllerApplication.get(), packageName) + } else true + } + + /** Group app op accesses by attribution label if it is available and user visible. */ + private suspend fun List<DiscretePackageOpsModel>.groupByAttributionLabelIfNeeded() = + map { packageOps -> + if (!shouldShowAttributionLabel(packageOps.packageName)) { + listOf(packageOps) + } else { + val attributionInfo = + packageRepository.getPackageAttributionInfo( + packageOps.packageName, + UserHandle.of(packageOps.userId), + ) + if (attributionInfo != null) { + if ( + attributionInfo.areUserVisible && attributionInfo.tagResourceMap != null + ) { + val attributionLabelOpsMap: Map<String?, List<DiscreteOpModel>> = + packageOps.appOpAccesses + .map { appOpEntry -> + val resourceId = + attributionInfo.tagResourceMap[ + appOpEntry.attributionTag] + val label = + attributionInfo.resourceLabelMap?.get(resourceId) + label to appOpEntry + } + .groupBy { labelAppOpEntryPair -> labelAppOpEntryPair.first } + .mapValues { (_, values) -> + values.map { labelAppOpEntryPair -> + labelAppOpEntryPair.second + } + } + + attributionLabelOpsMap.map { labelAppOpsEntry -> + DiscretePackageOpsModel( + packageOps.packageName, + packageOps.userId, + appOpAccesses = labelAppOpsEntry.value, + attributionLabel = labelAppOpsEntry.key, + isUserSensitive = packageOps.isUserSensitive, + ) + } + } else { + listOf(packageOps) + } + } else { + listOf(packageOps) + } + } + } + .flatten() + + /** + * App op accesses are merged if the timestamp of the access is within the cluster spacing i.e. + * one minute. + */ + private fun List<DiscretePackageOpsModel>.createAccessClusters(): + List<DiscretePackageOpsModel> { + return flatMap { packageOps -> + val clusters = mutableListOf<DiscretePackageOpsModel>() + val currentCluster = mutableListOf<DiscreteOpModel>() + val sortedOps = packageOps.appOpAccesses.sortedBy { it.accessTimeMillis } + for (discreteAccess in sortedOps) { + if (currentCluster.isEmpty()) { + currentCluster.add(discreteAccess) + } else if (!canAccessBeAddedToCluster(discreteAccess, currentCluster)) { + clusters.add( + DiscretePackageOpsModel( + packageOps.packageName, + packageOps.userId, + currentCluster.toMutableList(), + packageOps.attributionLabel, + packageOps.isUserSensitive, + ) + ) + currentCluster.clear() + currentCluster.add(discreteAccess) + } else { + currentCluster.add(discreteAccess) + } + } + + if (currentCluster.isNotEmpty()) { + clusters.add( + DiscretePackageOpsModel( + packageOps.packageName, + packageOps.userId, + currentCluster.toMutableList(), + packageOps.attributionLabel, + packageOps.isUserSensitive, + ) + ) + } + clusters + } + } + + private fun List<DiscretePackageOpsModel>.buildPermissionTimelineUsage(): + List<PermissionTimelineUsageModel> { + return this.map { packageOpsCluster -> + val startTimeMillis = packageOpsCluster.appOpAccesses.minOf { it.accessTimeMillis } + // The end minute is exclusive here in terms of access, i.e. [1..5) as the private data + // was not accessed at minute 5, it helps calculate the duration correctly. + val endTimeMillis = + packageOpsCluster.appOpAccesses.maxOf { appOpEvent -> + if (appOpEvent.durationMillis > 0) + appOpEvent.accessTimeMillis + appOpEvent.durationMillis + else appOpEvent.accessTimeMillis + ONE_MINUTE_MS + } + val durationMillis = endTimeMillis - startTimeMillis + val proxy = packageOpsCluster.appOpAccesses.firstOrNull { it.proxyPackageName != null } + + PermissionTimelineUsageModel( + packageOpsCluster.packageName, + packageOpsCluster.userId, + packageOpsCluster.appOpAccesses.map { it.opName }.toSet(), + startTimeMillis, + // Make the end time inclusive i.e. [1..4] + endTimeMillis - ONE_MINUTE_MS, + durationMillis, + packageOpsCluster.isUserSensitive, + packageOpsCluster.attributionLabel, + packageOpsCluster.appOpAccesses.mapNotNull { it.attributionTag }.toSet(), + proxy?.proxyPackageName, + proxy?.proxyUserId, + ) + } + } + + private fun isLocationByPassEnabled(): Boolean = + SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled() + + /** + * Determine if an op should be in its own cluster and hence display as an individual entry in + * the privacy timeline + */ + private fun isOpClusteredByItself(opName: String): Boolean { + if (isLocationByPassEnabled()) { + return opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION + } + return false + } + + private fun canAccessBeAddedToCluster( + currentAccess: DiscreteOpModel, + clusteredAccesses: List<DiscreteOpModel>, + ): Boolean { + val clusterOp = clusteredAccesses.last().opName + if ( + (isOpClusteredByItself(currentAccess.opName) || isOpClusteredByItself(clusterOp)) && + currentAccess.opName != clusteredAccesses.last().opName + ) { + return false + } + val currentAccessMinute = currentAccess.accessTimeMillis / ONE_MINUTE_MS + val prevMostRecentAccessMillis = + clusteredAccesses.maxOf { discreteAccess -> + if (discreteAccess.durationMillis > 0) + // accessTimeMillis and durationMillis are rounded at minute level. if an entry + // says mic was accessed for 3 minutes at minute 45, then the end time should + // be minute 47, as the mic was accessed at minute 45, 46, and 47. + // 45 + 3 - 1 = 47 + discreteAccess.accessTimeMillis + discreteAccess.durationMillis - ONE_MINUTE_MS + else discreteAccess.accessTimeMillis + } + val prevMostRecentAccessMinute = prevMostRecentAccessMillis / ONE_MINUTE_MS + return (currentAccessMinute - prevMostRecentAccessMinute) <= CLUSTER_SPACING_MINUTES + } + + private fun getGroupOfPlatformPermission(permission: String): String? { + if (permission == Manifest.permission.LOCATION_BYPASS) { + return Manifest.permission_group.LOCATION + } + return PermissionMapping.getGroupOfPlatformPermission(permission) + } + + /** + * Filter out packages that are not requesting any permission from the permission group anymore. + */ + private suspend fun List<DiscretePackageOpsModel>.filterPackagesNotRequestingPermission( + permissionGroup: String + ): List<DiscretePackageOpsModel> { + return mapNotNull { packageOpsModel -> + val userHandle = UserHandle.of(packageOpsModel.userId) + val packageInfo = + packageRepository.getPackageInfo(packageOpsModel.packageName, userHandle) + val isAnyPermissionRequestedFromPermissionGroup = + packageInfo?.requestedPermissions?.any { permission -> + permissionGroup == getGroupOfPlatformPermission(permission) + } ?: false + if (isAnyPermissionRequestedFromPermissionGroup) { + packageOpsModel + } else { + null + } + } + } + + companion object { + val permissionGroupToOpNames: Map<String, List<String>> = permissionGroupToOpNamesMap() + + private fun permissionGroupToOpNamesMap(): Map<String, List<String>> { + val permissionGroupOpNamesMap = mutableMapOf<String, MutableList<String>>() + val permissionGroups = + listOf( + Manifest.permission_group.CAMERA, + Manifest.permission_group.LOCATION, + Manifest.permission_group.MICROPHONE, + ) + permissionGroups.forEach { permissionGroup -> + val opNames = + PermissionMapping.getPlatformPermissionNamesOfGroup(permissionGroup) + .mapNotNull { AppOpsManager.permissionToOp(it) } + .toMutableList() + permissionGroupOpNamesMap[permissionGroup] = opNames + } + permissionGroupOpNamesMap[Manifest.permission_group.MICROPHONE]?.add( + AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE + ) + permissionGroupOpNamesMap[Manifest.permission_group.CAMERA]?.add( + AppOpsManager.OPSTR_PHONE_CALL_CAMERA + ) + if (SdkLevel.isAtLeastT()) { + permissionGroupOpNamesMap[Manifest.permission_group.MICROPHONE]?.add( + AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO + ) + } + if (isLocationByPassEnabled()) { + permissionGroupOpNamesMap[Manifest.permission_group.LOCATION]?.add( + AppOpsManager.OPSTR_EMERGENCY_LOCATION + ) + } + return permissionGroupOpNamesMap.toMap() + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt index 7e079949a..5faacc235 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt @@ -29,6 +29,7 @@ import com.android.permissioncontroller.appops.data.repository.v31.AppOpReposito import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository import com.android.permissioncontroller.permission.domain.model.v31.PackagePermissionGroupUsageModel import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModelWrapper import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository import com.android.permissioncontroller.role.data.repository.v31.RoleRepository @@ -51,18 +52,20 @@ class GetPermissionGroupUsageUseCase( * Returns a flow (i.e. a stream) of permission group usages (i.e. the private data accesses) * for privacy dashboard page. */ - operator fun invoke(): Flow<List<PermissionGroupUsageModel>> { + operator fun invoke(): Flow<PermissionGroupUsageModelWrapper> { return appOpRepository.packageAppOpsUsages.map { packagesOps -> val exemptedPackages = roleRepository.getExemptedPackages() val currentUsers = userRepository.getUserProfilesIncludingCurrentUser() - packagesOps - .mapToPermissionGroups() - .filter { it.userId in currentUsers } - .filter { it.packageName !in exemptedPackages } - .filterQuietProfilesIfNeeded(currentUsers) - .filterNonRequestedOps() - .buildPermissionGroupUsageModels() + val usages = + packagesOps + .mapToPermissionGroups() + .filter { it.userId in currentUsers } + .filter { it.packageName !in exemptedPackages } + .filterQuietProfilesIfNeeded(currentUsers) + .filterNonRequestedOps() + .buildPermissionGroupUsageModels() + PermissionGroupUsageModelWrapper.Success(usages) } } @@ -128,8 +131,7 @@ class GetPermissionGroupUsageUseCase( packageInfo?.requestedPermissions?.any { permission -> permissionGroupUsage.key == PermissionMapping.getGroupOfPlatformPermission(permission) - } - ?: false + } ?: false } if (filteredOps.isNotEmpty()) { PackagePermissionGroupUsageModel(pkgOps.packageName, filteredOps, pkgOps.userId) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/PermissionUsageUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/PermissionUsageUtils.kt new file mode 100644 index 000000000..7f5910ac1 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/PermissionUsageUtils.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.domain.usecase.v31 + +import android.Manifest +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.UserHandle +import android.permission.flags.Flags +import androidx.annotation.VisibleForTesting +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository +import com.android.permissioncontroller.permission.utils.PermissionMapping +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.user.data.repository.v31.UserRepository + +/** Returns a list of users which can be shown in the Settings surfaces. */ +suspend fun List<Int>.filterUsersToShowInQuietMode(userRepository: UserRepository): List<Int> { + if (!SdkLevel.isAtLeastV()) { + return this + } + val usersQuietModeEnabledMap = + this.associateWith { userId -> userRepository.isQuietModeEnabled(userId) } + val usersShouldShowInQuietModeMap = + this.associateWith { userId -> userRepository.shouldShowInQuietMode(userId) } + + return filter { + val isQuietModeEnabled = checkNotNull(usersQuietModeEnabledMap[it]) + val shouldShowInQuietMode = checkNotNull(usersShouldShowInQuietModeMap[it]) + !isQuietModeEnabled || shouldShowInQuietMode + } +} + +/** + * Determines if an app's permission group is user-sensitive. if the permission group is not user + * sensitive then its only shown when user choose `Show system` option + */ +suspend fun isPermissionGroupUserSensitive( + packageName: String, + permissionGroup: String, + userId: Int, + permissionRepository: PermissionRepository, + packageRepository: PackageRepository, +): Boolean { + if (isTelecomPackage(packageName, permissionGroup)) { + return false + } + val userHandle = UserHandle.of(userId) + val packageInfo = packageRepository.getPackageInfo(packageName, userHandle) ?: return false + // if not a system app, the permission group must be user sensitive + if (packageInfo.applicationFlags and ApplicationInfo.FLAG_SYSTEM == 0) { + return true + } + + packageInfo.requestedPermissions.forEachIndexed { index, permissionName -> + if (PermissionMapping.getGroupOfPlatformPermission(permissionName) == permissionGroup) { + val permFlags = + permissionRepository.getPermissionFlags(permissionName, packageName, userHandle) + val packageFlags = packageInfo.requestedPermissionsFlags[index] + val isPermissionGranted = + packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 && + permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0 + if (isPermissionUserSensitive(isPermissionGranted, permFlags)) { + return true + } + } + } + return false +} + +private fun isPermissionUserSensitive(isPermissionGranted: Boolean, permissionFlags: Int): Boolean { + return if (isPermissionGranted) { + permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0 + } else { + permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0 + } +} + +@VisibleForTesting const val TELECOM_PACKAGE = "com.android.server.telecom" + +private fun isTelecomPackage(packageName: String, permissionGroup: String): Boolean { + return packageName == TELECOM_PACKAGE && + (permissionGroup == Manifest.permission_group.CAMERA || + permissionGroup == Manifest.permission_group.MICROPHONE) +} + +fun isLocationByPassEnabled(): Boolean = + SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled() diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/package-info.java b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/package-info.java new file mode 100644 index 000000000..5a9df05f9 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.S) +package com.android.permissioncontroller.permission.domain.usecase.v31; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt index a5736ca83..61a604de8 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt @@ -20,6 +20,7 @@ import android.Manifest import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.os.Build import android.os.UserHandle +import com.android.permissioncontroller.permission.utils.Utils /** * A lightweight version of the AppPermissionGroup data structure. Represents information about a @@ -75,10 +76,13 @@ data class LightAppPermGroup( get() = permissions.mapNotNull { (name, _) -> if (name !in backgroundPermNames) name else null } + val isPlatformPermissionGroup = permGroupInfo.packageName == Utils.OS_PKG + val foreground = AppPermSubGroup( permissions.filter { it.key in foregroundPermNames }, packageInfo, + isPlatformPermissionGroup, specialLocationGrant ) @@ -86,6 +90,7 @@ data class LightAppPermGroup( AppPermSubGroup( permissions.filter { it.key in backgroundPermNames }, packageInfo, + isPlatformPermissionGroup, specialLocationGrant ) @@ -123,7 +128,7 @@ data class LightAppPermGroup( val isOneTime = (permGroupName != Manifest.permission_group.LOCATION && permissions.any { it.value.isOneTime } && - permissions.none { !it.value.isOneTime && it.value.isGrantedIncludingAppOp }) || + permissions.none { !it.value.isOneTime && it.value.isGranted }) || (permGroupName == Manifest.permission_group.LOCATION && permissions[ACCESS_COARSE_LOCATION]?.isOneTime == true) @@ -160,16 +165,24 @@ data class LightAppPermGroup( * * @param permissions The permissions contained within this subgroup, a subset of those * contained in the full group + * @param isPlatformPermissionGroup Whether this is a platform permission group * @param specialLocationGrant Whether this is a special location package */ data class AppPermSubGroup internal constructor( private val permissions: Map<String, LightPermission>, private val packageInfo: LightPackageInfo, + private val isPlatformPermissionGroup: Boolean, private val specialLocationGrant: Boolean? ) { /** Whether any of this App Permission SubGroup's permissions are granted */ - val isGranted = specialLocationGrant ?: permissions.any { it.value.isGrantedIncludingAppOp } + val isGranted = + specialLocationGrant + ?: permissions.any { + val mayGrantByPlatformOrSystem = + !isPlatformPermissionGroup || it.value.isPlatformOrSystem + it.value.isGranted && mayGrantByPlatformOrSystem + } /** * Whether this App Permission SubGroup should be treated as granted. This means either: @@ -178,14 +191,13 @@ data class LightAppPermGroup( * 2) All permissions were auto-granted (all permissions are all granted and all * RevokeWhenRequested.) */ - val isGrantedExcludingRWROrAllRWR = + val allowFullGroupGrant = specialLocationGrant ?: (permissions.any { - it.value.isGrantedIncludingAppOp && !it.value.isRevokeWhenRequested - } || - permissions.all { - it.value.isGrantedIncludingAppOp && it.value.isRevokeWhenRequested - }) + val mayGrantByPlatformOrSystem = + !isPlatformPermissionGroup || it.value.isPlatformOrSystem + it.value.allowFullGroupGrant && mayGrantByPlatformOrSystem + } || permissions.all { it.value.isGranted && it.value.isRevokeWhenRequested }) /** Whether any of this App Permission SubGroup's permissions are granted by default */ val isGrantedByDefault = permissions.any { it.value.isGrantedByDefault } @@ -196,7 +208,7 @@ data class LightAppPermGroup( */ val isOneTime = permissions.any { it.value.isOneTime } && - permissions.none { it.value.isGrantedIncludingAppOp && !it.value.isOneTime } + permissions.none { it.value.isGranted && !it.value.isOneTime } /** * Whether any of this App Permission Subgroup's foreground permissions are fixed by policy diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt index ab8afae08..32bc3c526 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt @@ -61,7 +61,9 @@ data class LightPackageInfo( pI: PackageInfo ) : this( pI.packageName, - pI.permissions?.map { perm -> LightPermInfo(perm) } ?: emptyList(), + pI.permissions?.map { perm -> + LightPermInfo(perm, pI.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM != 0) + } ?: emptyList(), pI.requestedPermissions?.toList() ?: emptyList(), pI.requestedPermissionsFlags?.toList() ?: emptyList(), pI.applicationInfo!!.uid, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt index c1d271098..a25dcc24b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt @@ -30,6 +30,7 @@ import android.content.pm.PermissionInfo * @param protection The protection level of this permission * @param protection Extra information about the protection of this permission * @param flags The system flags of this permission + * @param isSystem Whether this permission is defined by a system app */ data class LightPermInfo( val name: String, @@ -38,10 +39,12 @@ data class LightPermInfo( val backgroundPermission: String?, val protection: Int, val protectionFlags: Int, - val flags: Int + val flags: Int, + val isSystem: Boolean?, ) { constructor( - permInfo: PermissionInfo + permInfo: PermissionInfo, + isSystem: Boolean? ) : this( permInfo.name, permInfo.packageName, @@ -49,7 +52,8 @@ data class LightPermInfo( permInfo.backgroundPermission, permInfo.protection, permInfo.protectionFlags, - permInfo.flags + permInfo.flags, + isSystem ) /** 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 7492ea6e0..66a076551 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt @@ -28,8 +28,8 @@ import com.android.permissioncontroller.permission.utils.Utils * * @param pkgInfo The package requesting the permission * @param permInfo The permissionInfo this represents - * @param isGrantedIncludingAppOp Whether or not this permission is functionally granted. A - * non-granted app op but granted permission is counted as not granted + * @param isGranted Whether or not this permission is functionally granted. A non-granted app op but + * granted permission is counted as not granted * @param flags The PermissionController flags for this permission * @param foregroundPerms The foreground permission names corresponding to this permission, if this * permission is a background permission @@ -37,7 +37,7 @@ import com.android.permissioncontroller.permission.utils.Utils data class LightPermission( val pkgInfo: LightPackageInfo, val permInfo: LightPermInfo, - val isGrantedIncludingAppOp: Boolean, + val isGranted: Boolean, val flags: Int, val foregroundPerms: List<String>? ) { @@ -98,9 +98,9 @@ data class LightPermission( /** Whether this permission is user sensitive in its current grant state */ val isUserSensitive = !isRuntimePlatformPermission(permInfo.name) || - (isGrantedIncludingAppOp && + (isGranted && (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) || - (!isGrantedIncludingAppOp && + (!isGranted && (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0) /** Whether the permission is restricted */ val isRestricted = @@ -122,10 +122,17 @@ data class LightPermission( */ val isSelectedLocationAccuracy = flags and PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY != 0 + /** Whether this permission is defined by platform or a system app */ + val isPlatformOrSystem = permInfo.packageName == Utils.OS_PKG || permInfo.isSystem == true + /** + * Whether this permission is granted including app op and does not hold the + * PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED flag. + */ + val allowFullGroupGrant = isGranted && !isRevokeWhenRequested override fun toString() = buildString { append(name) - if (isGrantedIncludingAppOp) append(", Granted") else append(", NotGranted") + if (isGranted) append(", Granted") else append(", NotGranted") if (isPolicyFixed) append(", PolicyFixed") if (isSystemFixed) append(", SystemFixed") if (isUserFixed) append(", UserFixed") diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt index d5451c208..04cc8a796 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt @@ -155,6 +155,7 @@ data class LightHistoricalPackageOps( val opEntry: AttributedOpEntry = it.getDiscreteAccessAt(i) discreteAccessList.add( DiscreteAccess( + it.opName, opEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS), opEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS), opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS) @@ -192,6 +193,7 @@ data class LightHistoricalPackageOps( val attributedOpEntry: AttributedOpEntry = it.getDiscreteAccessAt(i) discreteAccessList.add( DiscreteAccess( + it.opName, attributedOpEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS), attributedOpEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS), attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS) @@ -231,6 +233,7 @@ data class LightHistoricalPackageOps( /** Data class representing a discrete permission access. */ data class DiscreteAccess( + val opName: String, val accessTimeMs: Long, val accessDurationMs: Long, val proxy: OpEventProxyInfo? diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt index cfe753019..aae2aa212 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt @@ -132,7 +132,7 @@ suspend fun revokeAppPermissions( val fixed = group.isBackgroundFixed || group.isForegroundFixed val granted = group.permissions.any { (_, perm) -> - perm.isGrantedIncludingAppOp && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS + perm.isGranted && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS } if ( !fixed && diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java index 24aab174c..2fa809c6d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java @@ -96,6 +96,7 @@ public class BackupHelper { private static final String ATTR_USER_SET = "set"; private static final String ATTR_USER_FIXED = "fixed"; private static final String ATTR_WAS_REVIEWED = "was-reviewed"; + private static final String ATTR_ONE_TIME = "one-time"; /** Flags of permissions to <u>not</u> back up */ private static final int SYSTEM_RUNTIME_GRANT_MASK = FLAG_PERMISSION_POLICY_FIXED @@ -452,19 +453,21 @@ public class BackupHelper { private final boolean mIsUserSet; private final boolean mIsUserFixed; private final boolean mWasReviewed; + private final boolean mIsOneTime; // Not persisted, used during parsing so explicitly defined state takes precedence private final boolean mIsAddedFromSplit; private BackupPermissionState(@NonNull String permissionName, boolean isGranted, boolean isUserSet, boolean isUserFixed, boolean wasReviewed, - boolean isAddedFromSplit) { + boolean isOneTime, boolean isAddedFromSplit) { mPermissionName = permissionName; mIsGranted = isGranted; mIsUserSet = isUserSet; mIsUserFixed = isUserFixed; mWasReviewed = wasReviewed; mIsAddedFromSplit = isAddedFromSplit; + mIsOneTime = isOneTime; } /** @@ -512,6 +515,7 @@ public class BackupHelper { "true".equals(parser.getAttributeValue(null, ATTR_USER_SET)), "true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED)), "true".equals(parser.getAttributeValue(null, ATTR_WAS_REVIEWED)), + "true".equals(parser.getAttributeValue(null, ATTR_ONE_TIME)), /* isAddedFromSplit */ i > 0)); } @@ -519,7 +523,8 @@ public class BackupHelper { } /** - * Is the permission granted, also considering the app-op. + * Is the permission granted, also considering the app-op. Don't consider one time grant + * as a permission grant for backup/restore. * * <p>This does not consider the review-required state of the permission. * @@ -528,7 +533,8 @@ public class BackupHelper { * @return {@code true} iff the permission and app-op is granted */ private static boolean isPermGrantedIncludingAppOp(@NonNull Permission perm) { - return perm.isGranted() && (!perm.affectsAppOp() || perm.isAppOpAllowed()); + return perm.isGranted() && (!perm.affectsAppOp() || perm.isAppOpAllowed()) + && !perm.isOneTime(); } /** @@ -549,7 +555,7 @@ public class BackupHelper { return null; } - if (!perm.isUserSet() && perm.isGrantedByDefault()) { + if (!perm.isUserSet() && !perm.isOneTime() && perm.isGrantedByDefault()) { return null; } @@ -564,10 +570,10 @@ public class BackupHelper { } if (isNotInDefaultGrantState || perm.isUserSet() || perm.isUserFixed() - || permissionWasReviewed) { + || perm.isOneTime() || permissionWasReviewed) { return new BackupPermissionState(perm.getName(), isPermGrantedIncludingAppOp(perm), perm.isUserSet(), perm.isUserFixed(), permissionWasReviewed, - /* isAddedFromSplit */ false); + perm.isOneTime(), /* isAddedFromSplit */ false); } else { return null; } @@ -628,6 +634,10 @@ public class BackupHelper { serializer.attribute(null, ATTR_WAS_REVIEWED, "true"); } + if (mIsOneTime) { + serializer.attribute(null, ATTR_ONE_TIME, "true"); + } + serializer.endTag(null, TAG_PERMISSION); } @@ -671,6 +681,10 @@ public class BackupHelper { perm.setUserSet(mIsUserSet); } + + if (!perm.isOneTime()) { + perm.setOneTime(mIsOneTime); + } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java index eb78414f9..e3deb58a7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java @@ -861,6 +861,7 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL group.setSelfRevoked(); group.persistChanges(false); } + Log.d(LOG_TAG, "Self revoked permissions: " + String.join(",", permissions)); getMainExecutor().execute(callback); } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt index af3b60702..85145f346 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt @@ -492,7 +492,7 @@ object RuntimePermissionsUpgradeController { LightPermission( perm.pkgInfo, perm.permInfo, - perm.isGrantedIncludingAppOp, + perm.isGranted, perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, perm.foregroundPerms ) @@ -566,10 +566,11 @@ object RuntimePermissionsUpgradeController { appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue if ( + !perm.isGranted && !perm.isUserSet && - !perm.isSystemFixed && - !perm.isPolicyFixed && - !perm.isGrantedIncludingAppOp + !perm.isOneTime && + !perm.isSystemFixed && + !perm.isPolicyFixed ) { grants.add( Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION)) @@ -610,20 +611,21 @@ object RuntimePermissionsUpgradeController { // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following: // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL - val storageAppPermGroups = + val grantedStorageAppPermGroups = storageAndMediaAppPermGroups.filter { it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU && it.permGroupInfo.name == permission_group.STORAGE && it.isGranted && it.isUserSet } - for (storageAppPermGroup in storageAppPermGroups) { + for (storageAppPermGroup in grantedStorageAppPermGroups) { val pkgName = storageAppPermGroup.packageInfo.packageName val auralAppPermGroup = storageAndMediaAppPermGroups.firstOrNull { it.packageInfo.packageName == pkgName && it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL && !it.isUserSet && + !it.isOneTime && !it.isUserFixed } val visualAppPermGroup = @@ -632,7 +634,8 @@ object RuntimePermissionsUpgradeController { it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL && !it.permissions .filter { it.key != permission.ACCESS_MEDIA_LOCATION } - .any { it.value.isUserSet || it.value.isUserFixed } + .any { it.value.isUserSet || it.value.isOneTime || + it.value.isUserFixed } } if (auralAppPermGroup != null) { @@ -670,7 +673,7 @@ object RuntimePermissionsUpgradeController { LightPermission( perm.pkgInfo, perm.permInfo, - perm.isGrantedIncludingAppOp, + perm.isGranted, perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, perm.foregroundPerms ) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING index 487701204..dc1bb28a5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING @@ -1,20 +1,6 @@ { "presubmit": [ { - "name": "CtsPermissionTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-filter": "android.permission.cts.PermissionControllerTest" - }, - { - "include-filter": "android.permission.cts.OneTimePermissionTest" - } - ] - }, - { "name": "CtsPermissionUiTestCases", "options": [ { @@ -26,18 +12,6 @@ ] }, { - "name": "CtsPermissionTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-filter": "android.permission.cts.LocationAccessCheckTest" - } - ], - "file_patterns": ["LocationAccessCheck\\.java"] - }, - { "name": "CtsBackupTestCases", "options": [ { 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 a69b78a06..5ba19f4c0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt @@ -70,7 +70,6 @@ class SafetyCenterQsTileService : TileService() { qsTile.label = getString(R.string.safety_privacy_qs_tile_title) qsTile.subtitle = getString(R.string.safety_privacy_qs_tile_subtitle) qsTile.contentDescription = TextUtils.concat(qsTile.label, ", ", qsTile.subtitle) - qsTile.state = Tile.STATE_ACTIVE qsTile.updateTile() } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt index 58536b796..715e3823d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt @@ -111,10 +111,6 @@ class SafetyLabelChangesJobService : JobService() { Log.i(LOG_TAG, "onReceive: Safety label change notifications are not enabled.") return } - if (KotlinUtils.safetyLabelChangesJobServiceKillSwitch()) { - Log.i(LOG_TAG, "onReceive: kill switch is set.") - return - } if (isContextInProfileUser(receiverContext)) { Log.i( LOG_TAG, @@ -174,10 +170,6 @@ class SafetyLabelChangesJobService : JobService() { Log.w(LOG_TAG, "Not starting job: safety label change notifications are not enabled.") return false } - if (KotlinUtils.safetyLabelChangesJobServiceKillSwitch()) { - Log.i(LOG_TAG, "Not starting job: kill switch is set.") - return false - } when (params.jobId) { SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID -> { dispatchDetectUpdatesJob(params) @@ -490,8 +482,7 @@ class SafetyLabelChangesJobService : JobService() { private suspend fun getNumberOfAppsWithDataSharingChanged(): Int { val appDataSharingUpdates = AppDataSharingUpdatesLiveData(PermissionControllerApplication.get()) - .getInitializedValue() - ?: return 0 + .getInitializedValue() ?: return 0 return appDataSharingUpdates .map { appDataSharingUpdate -> @@ -523,8 +514,7 @@ class SafetyLabelChangesJobService : JobService() { } ?.keys ?.filter { packageUser: Pair<String, UserHandle> -> packageUser.first == packageName } - ?.map { packageUser: Pair<String, UserHandle> -> packageUser.second } - ?: listOf() + ?.map { packageUser: Pair<String, UserHandle> -> packageUser.second } ?: listOf() } private fun AppDataSharingUpdate.containsLocationCategoryUpdate() = @@ -660,9 +650,7 @@ class SafetyLabelChangesJobService : JobService() { SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID, ComponentName(context, SafetyLabelChangesJobService::class.java) ) - .setRequiresDeviceIdle( - KotlinUtils.runSafetyLabelChangesJobOnlyWhenDeviceIdle() - ) + .setRequiresDeviceIdle(true) .build() val result = jobScheduler.schedule(job) if (result != JobScheduler.RESULT_SUCCESS) { @@ -693,9 +681,7 @@ class SafetyLabelChangesJobService : JobService() { SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID, ComponentName(context, SafetyLabelChangesJobService::class.java) ) - .setRequiresDeviceIdle( - KotlinUtils.runSafetyLabelChangesJobOnlyWhenDeviceIdle() - ) + .setRequiresDeviceIdle(true) .setPeriodic(KotlinUtils.getSafetyLabelChangesJobIntervalMillis()) .setPersisted(true) .build() diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java index a4f629d80..c1479caf2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java @@ -46,7 +46,6 @@ import android.app.KeyguardManager; import android.app.ecm.EnhancedConfirmationManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.res.Resources; @@ -195,7 +194,7 @@ public class GrantPermissionsActivity extends SettingsActivity /** A list of permissions requested on an app's behalf by the system. Usually Implicitly * requested, although this isn't necessarily always the case. */ - private List<String> mSystemRequestedPermissions = new ArrayList<>(); + private final List<String> mSystemRequestedPermissions = new ArrayList<>(); /** A copy of the list of permissions originally requested in the intent to this activity */ private String[] mOriginalRequestedPermissions = new String[0]; @@ -209,7 +208,7 @@ public class GrantPermissionsActivity extends SettingsActivity * A list of other GrantPermissionActivities for the same package which passed their list of * permissions to this one. They need to be informed when this activity finishes. */ - private List<GrantPermissionsActivity> mFollowerActivities = new ArrayList<>(); + private final List<GrantPermissionsActivity> mFollowerActivities = new ArrayList<>(); /** Whether this activity has asked another GrantPermissionsActivity to show on its behalf */ private boolean mDelegated; @@ -235,7 +234,7 @@ public class GrantPermissionsActivity extends SettingsActivity private PackageManager mPackageManager; - private ActivityResultLauncher<Intent> mShowWarningDialog = + private final ActivityResultLauncher<Intent> mShowWarningDialog = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { @@ -284,7 +283,7 @@ public class GrantPermissionsActivity extends SettingsActivity return; } try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(mTargetPackage, 0); + mPackageManager.getPackageInfo(mTargetPackage, 0); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Unable to get package info for the calling package.", e); finishAfterTransition(); @@ -314,20 +313,23 @@ public class GrantPermissionsActivity extends SettingsActivity .getPackageManager(); } - // When the dialog is streamed to a remote device, verify requested permissions are all - // device aware and target device is the same as the remote device. Otherwise show a - // warning dialog. + // When the permission grant dialog is streamed to a virtual device, and when requested + // permissions include both device-aware permissions and non-device aware permissions, + // device-aware permissions will use virtual device id and non-device aware permissions + // will use default device id for granting. If flag is not enabled, we would show a + // warning dialog for this use case. if (getDeviceId() != ContextCompat.DEVICE_ID_DEFAULT) { boolean showWarningDialog = mTargetDeviceId != getDeviceId(); for (String permission : mRequestedPermissions) { - if (!MultiDeviceUtils.isPermissionDeviceAware( - getApplicationContext(), mTargetDeviceId, permission)) { + if (!MultiDeviceUtils.isPermissionDeviceAware(getApplicationContext(), + mTargetDeviceId, permission)) { showWarningDialog = true; + break; } } - if (showWarningDialog) { + if (showWarningDialog && !Flags.allowHostPermissionDialogsOnVirtualDevices()) { mShowWarningDialog.launch( new Intent(this, PermissionDialogStreamingBlockedActivity.class)); return; @@ -1115,9 +1117,17 @@ public class GrantPermissionsActivity extends SettingsActivity if ((mDelegated || (mViewModel != null && mViewModel.shouldReturnPermissionState())) && mTargetPackage != null) { + PackageManager defaultDevicePackageManager = SdkLevel.isAtLeastV() + && mTargetDeviceId != ContextCompat.DEVICE_ID_DEFAULT + ? createDeviceContext(ContextCompat.DEVICE_ID_DEFAULT).getPackageManager() + : mPackageManager; + PackageManager targetDevicePackageManager = mPackageManager; for (int i = 0; i < resultPermissions.length; i++) { - grantResults[i] = - mPackageManager.checkPermission(resultPermissions[i], mTargetPackage); + String permission = resultPermissions[i]; + PackageManager pm = MultiDeviceUtils.isPermissionDeviceAware( + getApplicationContext(), mTargetDeviceId, permission) + ? targetDevicePackageManager : defaultDevicePackageManager; + grantResults[i] = pm.checkPermission(resultPermissions[i], mTargetPackage); } } else { grantResults = new int[0]; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java index df5b98ae7..36917fbf1 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java @@ -61,6 +61,7 @@ import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.permissioncontroller.R; import com.android.permissioncontroller.hibernation.HibernationPolicyKt; +import com.android.permissioncontroller.permission.compat.AppPermissionFragmentCompat; import com.android.permissioncontroller.permission.ui.auto.AutoAllAppPermissionsFragment; import com.android.permissioncontroller.permission.ui.auto.AutoAppPermissionsFragment; import com.android.permissioncontroller.permission.ui.auto.AutoManageStandardPermissionsFragment; @@ -69,7 +70,6 @@ import com.android.permissioncontroller.permission.ui.auto.AutoReviewPermissionD import com.android.permissioncontroller.permission.ui.auto.AutoUnusedAppsFragment; import com.android.permissioncontroller.permission.ui.auto.dashboard.AutoPermissionUsageDetailsFragment; import com.android.permissioncontroller.permission.ui.auto.dashboard.AutoPermissionUsageFragment; -import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment; import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment; import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment; import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionDetailsWrapperFragment; @@ -154,6 +154,17 @@ public final class ManagePermissionsActivity extends SettingsActivity { // fragments are restored properly on configuration changes. setTheme(R.style.CarSettings); } + if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(this)) { + switch (getIntent().getAction()) { + case Intent.ACTION_MANAGE_PERMISSIONS: + case Intent.ACTION_MANAGE_APP_PERMISSION: + case Intent.ACTION_MANAGE_APP_PERMISSIONS: + case APP_PERMISSIONS_SETTINGS: + case Intent.ACTION_MANAGE_PERMISSION_APPS: + getTheme().applyStyle(R.style.ThemeOverlay_PermissionSettings, true); + break; + } + } super.onCreate(savedInstanceState); // TODO(b/309578419): Make this activity handle insets properly and then remove this. @@ -259,7 +270,7 @@ public final class ManagePermissionsActivity extends SettingsActivity { groupName, showSystem, sessionId); } else if (DeviceUtils.isWear(this)) { androidXFragment = WearPermissionUsageDetailsFragment - .newInstance(groupName, showSystem, show7Days); + .newInstance(groupName, showSystem); } else { androidXFragment = PermissionDetailsWrapperFragment .newInstance(groupName, Long.MAX_VALUE, showSystem, sessionId, @@ -312,8 +323,8 @@ public final class ManagePermissionsActivity extends SettingsActivity { args = WearAppPermissionFragment.createArgs(packageName, permissionName, groupName, userHandle, caller, sessionId, null); } else { - args = AppPermissionFragment.createArgs(packageName, permissionName, - groupName, userHandle, caller, sessionId, null); + args = AppPermissionFragmentCompat.createArgs(packageName, permissionName, + groupName, userHandle, caller, sessionId, null, null); } setNavGraph(args, R.id.app_permission); return; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING index d0b115bcb..507f19a15 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING @@ -1,17 +1,4 @@ { - "presubmit": [ - { - "name": "CtsPermissionTestCases", - "options": [ - { - "include-filter": "android.permission.cts.OneTimePermissionTest" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - } - ], "presubmit-large": [ { "name": "CtsDevicePolicyManagerTestCases", diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java index 1f30572ad..026f108fa 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java @@ -22,6 +22,8 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND; 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__GRANT_FINE_LOCATION; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; @@ -42,6 +44,7 @@ import android.text.BidiFormatter; import android.util.Log; import android.view.View; import android.widget.RadioButton; +import android.widget.Switch; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -95,6 +98,9 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment private TwoStatePreference mForegroundOnlyPermissionPreference; @NonNull private TwoStatePreference mDenyPermissionPreference; + + @NonNull + private TwoStatePreference mToggleFineLocationPreference; @NonNull private AutoTwoTargetPreference mDetailsPreference; @@ -127,9 +133,9 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment * Returns a new {@link AutoAppPermissionFragment}. * * @param packageName the package name for which the permission is being changed - * @param permName the name of the permission being changed - * @param groupName the name of the permission group being changed - * @param userHandle the user for which the permission is being changed + * @param permName the name of the permission being changed + * @param groupName the name of the permission group being changed + * @param userHandle the user for which the permission is being changed */ @NonNull public static AutoAppPermissionFragment newInstance(@NonNull String packageName, @@ -157,6 +163,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME); if (mPermGroupName == null) { @@ -214,6 +221,29 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment mDenyPermissionPreference.setTitle(R.string.app_permission_button_deny); permissionSelector.addPreference(mDenyPermissionPreference); + + Log.i(LOG_TAG, "enableCoarseFineLocationPromptForAaos flag set to: " + + Flags.enableCoarseFineLocationPromptForAaos()); + if (Flags.enableCoarseFineLocationPromptForAaos()) { + mToggleFineLocationPreference = new SelectedSwitchPermissionPreference( + requireContext()); + mToggleFineLocationPreference.setTitle(R.string.app_permission_location_accuracy); + mToggleFineLocationPreference.setSummary( + R.string.app_permission_location_accuracy_subtitle); + permissionSelector.addPreference(mToggleFineLocationPreference); + + mToggleFineLocationPreference.setOnPreferenceClickListener(v -> { + if (mToggleFineLocationPreference.isChecked()) { + requestChange(ChangeRequest.GRANT_FINE_LOCATION, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION); + } else { + requestChange(ChangeRequest.REVOKE_FINE_LOCATION, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION); + } + return true; + }); + } + mAllowPermissionPreference.setOnPreferenceClickListener(v -> { checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW); setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS); @@ -244,6 +274,8 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment }); mDetailsPreference = new AutoTwoTargetPreference(requireContext()); + // If the details are updated, setDetail will update the visibility + mDetailsPreference.setVisible(false); screen.addPreference(mDetailsPreference); } @@ -294,6 +326,16 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment } } } + + if (Flags.enableCoarseFineLocationPromptForAaos()) { + mViewModel.getButtonStateLiveData().observe(this, buttonState -> { + AppPermissionViewModel.ButtonState locationAccuracyState = buttonState.get( + AppPermissionViewModel.ButtonType.LOCATION_ACCURACY); + mToggleFineLocationPreference.setVisible(locationAccuracyState.isShown()); + mToggleFineLocationPreference.setChecked(locationAccuracyState.isChecked()); + mToggleFineLocationPreference.setEnabled(locationAccuracyState.isEnabled()); + }); + } } @Override @@ -498,6 +540,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment } else { mDetailsPreference.setSummary(detailResIds.getFirst()); } + mDetailsPreference.setVisible(true); } /** @@ -552,6 +595,26 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment } } + private static class SelectedSwitchPermissionPreference extends TwoStatePreference { + + SelectedSwitchPermissionPreference(Context context) { + super(context, null, + TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle, + android.R.attr.preferenceStyle)); + setPersistent(false); + setLayoutResource(R.layout.car_switch_button_preference); + setWidgetLayoutResource(R.layout.switch_button_preference_widget); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + Switch switchButton = (Switch) holder.findViewById(R.id.location_accuracy_switch); + switchButton.setChecked(isChecked()); + } + } + /** * A dialog warning the user that they are about to deny a permission that was granted by * default. @@ -564,7 +627,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment + ".arg.changeRequest"; private static final String BUTTON = ConfirmDialog.class.getName() + ".arg.button"; - private static int sCode = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; + private static int sCode = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; @NonNull @Override @@ -611,6 +674,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment } @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void showAdvancedConfirmDialog(AdvancedConfirmDialogArgs args) { AlertDialog.Builder b = new AlertDialog.Builder(getContext()) .setIcon(args.getIconId()) 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 446d97c3e..2c4f76eee 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java @@ -145,8 +145,7 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment implem if (SdkLevel.isAtLeastS()) { mPermissionUsages = new PermissionUsages(getContext()); - long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled() - ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 : + long aggregateDataFilterBeginDays = AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1; long filterTimeBeginMillis = Math.max(System.currentTimeMillis() diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoLocationPermissionPromptView.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoLocationPermissionPromptView.java new file mode 100644 index 000000000..aee72c295 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoLocationPermissionPromptView.java @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.auto; + +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; + +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE; +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 android.app.Activity; +import android.content.Context; +import android.graphics.Typeface; +import android.graphics.drawable.Icon; +import android.text.method.LinkMovementMethod; +import android.transition.ChangeBounds; +import android.transition.TransitionManager; +import android.util.SparseIntArray; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.AnimationUtils; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; + +import androidx.annotation.RawRes; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; + +import com.airbnb.lottie.LottieComposition; +import com.airbnb.lottie.LottieCompositionFactory; +import com.airbnb.lottie.LottieDrawable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An extension of {@link GrantPermissionsAutoViewHandler} that updates the current auto location + * permission prompt with a custom view + */ +public class AutoLocationPermissionPromptView implements View.OnClickListener { + private static final int[] LOCATION_ACCURACY_DIALOGS = + { + DIALOG_WITH_BOTH_LOCATIONS, + DIALOG_WITH_FINE_LOCATION_ONLY, + DIALOG_WITH_COARSE_LOCATION_ONLY + }; + private static final long ANIMATION_DURATION_MILLIS = 200; + private static final float LOCATION_IMAGE_SIZE_MODIFIER = 2; + private final Context mContext; + private GrantPermissionsViewHandler.ResultListener mResultListener; + private String mGroupName; + private Icon mGroupIcon; + private CharSequence mGroupMessage; + private CharSequence mDetailMessage; + private boolean[] mButtonVisibilities; + private boolean[] mLocationVisibilities; + private SparseIntArray mButtonResIdToNum = new SparseIntArray(); + private SparseIntArray mLocationResIdToNum = new SparseIntArray(); + private boolean mIsLocationPermissionDialogActionClicked = false; + private LottieDrawable mCoarseOffDrawable = null; + private LottieDrawable mCoarseOnDrawable = null; + private LottieDrawable mFineOffDrawable = null; + private LottieDrawable mFineOnDrawable = null; + + // Views + private ViewGroup mRootView = null; + private TextView mMessageView = null; + private TextView mDetailedMessageView = null; + private Button[] mButtons = new Button[NEXT_BUTTON]; + private View[] mLocationViews = new View[NEXT_LOCATION_DIALOG]; + + // Dialog Config + private RadioButton mCoarseRadioButton = null; + private RadioButton mFineRadioButton = null; + private int mSelectedPrecision = 0; + private float mLocationAccuracyImageDiameter = 0; + + /** + * Constructs a new AutoLocationPermissionPromptView. + * + * @param context the Context in which the view is created + */ + public AutoLocationPermissionPromptView(Context context) { + mContext = context; + generateResIdToButtonMap(); + mLocationAccuracyImageDiameter = + mContext.getResources().getDimension(R.dimen.location_accuracy_image_size) + + LOCATION_IMAGE_SIZE_MODIFIER; + } + + /** + * Sets the ResultListener for this view. The ResultListener will be notified when + * the user makes a selection in the permission prompt. + * + * @param listener the ResultListener to set + */ + public void setResultListener(GrantPermissionsViewHandler.ResultListener listener) { + mResultListener = listener; + } + + /** + * Creates and initializes the custom view for the auto location permission prompt. + * This method inflates the layout, sets up various UI elements, and configures their behaviors + * such as click listeners and gravity adjustments. + */ + public View createView() { + ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.auto_grant_permissions_material3, null); + + this.mRootView = rootView; + + int gravity = rootView.requireViewById(R.id.grant_singleton).getForegroundGravity(); + int verticalGravity = Gravity.VERTICAL_GRAVITY_MASK & gravity; + if (mContext instanceof Activity activity) { + Window window = activity.getWindow(); + window.setGravity(Gravity.CENTER_HORIZONTAL | verticalGravity); + } + + rootView.requireViewById(R.id.grant_singleton).setOnClickListener(this); + rootView.requireViewById(R.id.grant_dialog).setOnClickListener(this); + + mMessageView = rootView.requireViewById(R.id.permission_message); + mDetailedMessageView = rootView.requireViewById(R.id.detail_message); + mDetailedMessageView.setMovementMethod(LinkMovementMethod.getInstance()); + + Button[] buttons = new Button[NEXT_BUTTON]; + int numButtons = mButtonResIdToNum.size(); + for (int i = 0; i < numButtons; i++) { + Button button = rootView.requireViewById(mButtonResIdToNum.keyAt(i)); + button.setOnClickListener(this); + buttons[mButtonResIdToNum.valueAt(i)] = button; + } + this.mButtons = buttons; + + View[] locationViews = new View[NEXT_LOCATION_DIALOG]; + for (int i = 0; i < mLocationResIdToNum.size(); i++) { + View locationView = rootView.requireViewById(mLocationResIdToNum.keyAt(i)); + locationViews[mLocationResIdToNum.valueAt(i)] = locationView; + } + + initializeAnimatedImages(); + + mCoarseRadioButton = (RadioButton) locationViews[COARSE_RADIO_BUTTON]; + mFineRadioButton = (RadioButton) locationViews[FINE_RADIO_BUTTON]; + mCoarseRadioButton.setOnClickListener(this); + mFineRadioButton.setOnClickListener(this); + this.mLocationViews = locationViews; + + if (mDetailMessage != null) { + updateAll(); + } + return rootView; + } + + private void initializeAnimatedImages() { + mCoarseOffDrawable = getLottieDrawable(R.raw.coarse_loc_off); + mCoarseOnDrawable = getLottieDrawable(R.raw.coarse_loc_on); + mFineOffDrawable = getLottieDrawable(R.raw.fine_loc_off); + mFineOnDrawable = getLottieDrawable(R.raw.fine_loc_on); + } + + private LottieDrawable getLottieDrawable(@RawRes int rawResId) { + LottieComposition composition = LottieCompositionFactory + .fromRawResSync(mContext, rawResId).getValue(); + if (composition == null) { + throw new NullPointerException(); + } + + float scale = mLocationAccuracyImageDiameter / composition.getBounds().width(); + LottieDrawable drawable = new LottieDrawable() { + @Override + public int getIntrinsicHeight() { + return ((int) (super.getIntrinsicHeight() * scale)); + } + + @Override + public int getIntrinsicWidth() { + return ((int) (super.getIntrinsicWidth() * scale)); + } + }; + drawable.setComposition(composition); + return drawable; + } + + /** + * Updates the state of the view with the provided parameters. This method is called to refresh + * the UI elements with new data, ensuring that the view reflects the latest information. + */ + public void updateState(String groupName, Icon icon, + CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, + boolean[] locationVisibilities, int selectedPrecision) { + + mGroupName = groupName; + mGroupIcon = icon; + mGroupMessage = message; + mDetailMessage = detailMessage; + mSelectedPrecision = selectedPrecision; + setButtonVisibilities(buttonVisibilities); + setLocationVisibilities(locationVisibilities); + + // If the iconView is null, it's likely other related views are also null. Attempting to + // update variables in this state could lead to a null object reference and crash the app. + if (mMessageView != null) { + updateAll(); + } + } + + private void updateAll() { + updateDescription(); + updateDetailDescription(); + updateButtons(); + updateLocationVisibilities(); + + ChangeBounds growShrinkToNewContentSize = new ChangeBounds(); + growShrinkToNewContentSize.setDuration(ANIMATION_DURATION_MILLIS); + growShrinkToNewContentSize.setInterpolator( + AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in)); + TransitionManager.beginDelayedTransition(mRootView, growShrinkToNewContentSize); + } + + private void updateLocationVisibilities() { + if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]) { + if (mIsLocationPermissionDialogActionClicked) { + return; + } + mLocationViews[LOCATION_ACCURACY_LAYOUT].setVisibility(View.VISIBLE); + for (int i : LOCATION_ACCURACY_DIALOGS) { + if (mLocationVisibilities[i]) { + mLocationViews[i].setVisibility(View.VISIBLE); + } else { + mLocationViews[i].setVisibility(View.GONE); + } + } + + if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) { + mCoarseRadioButton.setVisibility(View.VISIBLE); + mFineRadioButton.setVisibility(View.VISIBLE); + if (mSelectedPrecision == 0) { + mFineRadioButton.setChecked(mLocationVisibilities[FINE_RADIO_BUTTON]); + mCoarseRadioButton.setChecked(mLocationVisibilities[COARSE_RADIO_BUTTON]); + } else { + mFineRadioButton.setChecked(mSelectedPrecision == FINE_RADIO_BUTTON); + mCoarseRadioButton.setChecked(mSelectedPrecision == COARSE_RADIO_BUTTON); + } + if (mCoarseRadioButton.isChecked()) { + runLocationAccuracyAnimation(false); + + } else if (mFineRadioButton.isChecked()) { + runLocationAccuracyAnimation(true); + } + } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) { + ((ImageView) mLocationViews[DIALOG_WITH_COARSE_LOCATION_ONLY]) + .setImageDrawable(mCoarseOnDrawable); + mCoarseOnDrawable.start(); + } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { + ((ImageView) mLocationViews[DIALOG_WITH_FINE_LOCATION_ONLY]) + .setImageDrawable(mFineOnDrawable); + mFineOnDrawable.start(); + } + } else { + mLocationViews[LOCATION_ACCURACY_LAYOUT].setVisibility(View.GONE); + for (int i : LOCATION_ACCURACY_DIALOGS) { + mLocationVisibilities[i] = false; + mLocationViews[i].setVisibility(View.GONE); + } + } + } + + private void runLocationAccuracyAnimation(boolean isFineSelected) { + if (isFineSelected) { + mCoarseOnDrawable.stop(); + mFineOffDrawable.stop(); + // Sets the drawable to appear to the left, top, right, or bottom of the text + mCoarseRadioButton.setCompoundDrawablesWithIntrinsicBounds( + /* left */ null, + /* top */ mCoarseOffDrawable, + /* right */ null, + /* bottom */ null + ); + mFineRadioButton.setCompoundDrawablesWithIntrinsicBounds( + /* left */ null, + /* top */ mFineOnDrawable, + /* right */ null, + /* bottom */ null + ); + mCoarseOffDrawable.start(); + mFineOnDrawable.start(); + mFineRadioButton.setTypeface(null, Typeface.BOLD); + mCoarseRadioButton.setTypeface(null, Typeface.NORMAL); + } else { + mCoarseOffDrawable.stop(); + mFineOnDrawable.stop(); + mCoarseRadioButton.setCompoundDrawablesWithIntrinsicBounds( + /* left */ null, + /* top */ mCoarseOnDrawable, + /* right */ null, + /* bottom */ null + ); + mFineRadioButton.setCompoundDrawablesWithIntrinsicBounds( + /* left */ null, + /* top */ mFineOffDrawable, + /* right */ null, + /* bottom */ null + ); + mFineOffDrawable.start(); + mCoarseOnDrawable.start(); + mCoarseRadioButton.setTypeface(null, Typeface.BOLD); + mFineRadioButton.setTypeface(null, Typeface.NORMAL); + } + } + + private void updateButtons() { + for (int i = 0; i < mButtonResIdToNum.size(); i++) { + int pos = mButtonResIdToNum.valueAt(i); + if (pos < mButtonVisibilities.length && mButtonVisibilities[pos]) { + mButtons[pos].setVisibility(View.VISIBLE); + } else { + mButtons[pos].setVisibility(View.GONE); + } + if (pos == ALLOW_FOREGROUND_BUTTON && mButtonVisibilities[pos]) { + if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT] + && mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] + ) { + mButtons[pos].setText(mContext.getResources().getString( + R.string.grant_dialog_button_change_to_precise_location)); + } else { + mButtons[pos].setText(mContext.getResources().getString( + R.string.grant_dialog_button_allow_foreground)); + } + } + if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON)) { + if ( + mLocationVisibilities[LOCATION_ACCURACY_LAYOUT] + && mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] + ) { + mButtons[pos].setText(mContext.getResources().getString( + R.string.grant_dialog_button_keey_approximate_location)); + } else { + mButtons[pos].setText( + mContext.getResources().getString(R.string.grant_dialog_button_deny)); + } + } + mButtons[pos].requestLayout(); + } + } + + private void updateDetailDescription() { + if (mDetailMessage == null) { + mDetailedMessageView.setVisibility(View.GONE); + } else { + mDetailedMessageView.setText(mDetailMessage); + mDetailedMessageView.setVisibility(View.VISIBLE); + } + } + + private void updateDescription() { + mMessageView.setText(mGroupMessage); + } + + private void setButtonVisibilities(boolean[] visibilities) { + // If GrantPermissionsActivity sent the user directly to settings, button visibilities are + // not created. If the activity was then destroyed by the system, once the activity is + // recreated to perform onActivityResult, it will try to loadInstanceState in onCreate but + // the button visibilities were never set, so they will be null. + mButtonVisibilities = visibilities == null ? new boolean[0] : visibilities; + } + + private void setLocationVisibilities(boolean[] visibilities) { + mLocationVisibilities = visibilities == null ? new boolean[0] : visibilities; + } + + @Override + public void onClick(View view) { + int id = view.getId(); + + if (id == R.id.permission_location_accuracy_radio_fine) { + if (mSelectedPrecision != FINE_RADIO_BUTTON) { + ((RadioButton) mLocationViews[FINE_RADIO_BUTTON]).setChecked(true); + mSelectedPrecision = FINE_RADIO_BUTTON; + runLocationAccuracyAnimation(true); + } + return; + } + + if (id == R.id.permission_location_accuracy_radio_coarse) { + if (mSelectedPrecision != COARSE_RADIO_BUTTON) { + ((RadioButton) mLocationViews[COARSE_RADIO_BUTTON]).setChecked(true); + mSelectedPrecision = COARSE_RADIO_BUTTON; + runLocationAccuracyAnimation(false); + } + return; + } + + if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]) { + mIsLocationPermissionDialogActionClicked = true; + } + + if (id == R.id.grant_singleton) { + mResultListener.onPermissionGrantResult(mGroupName, CANCELED); + return; + } + + List<String> affectedForegroundPermissions = new ArrayList<>(); + if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) { + if (((RadioGroup) mLocationViews[DIALOG_WITH_BOTH_LOCATIONS]) + .getCheckedRadioButtonId() == R.id.permission_location_accuracy_radio_coarse) { + + affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION); + } else if (((RadioGroup) mLocationViews[DIALOG_WITH_BOTH_LOCATIONS]) + .getCheckedRadioButtonId() == R.id.permission_location_accuracy_radio_fine) { + + affectedForegroundPermissions.add(ACCESS_FINE_LOCATION); + affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION); + } + } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { + affectedForegroundPermissions.add(ACCESS_FINE_LOCATION); + } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) { + affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION); + } + + + Map<Integer, Integer> buttonToResultValueMap = initializeButtonToResultValueMap(); + int resultValue = buttonToResultValueMap.getOrDefault( + mButtonResIdToNum.get(id, -1), -1); + if (resultValue != -1) { + if (resultValue != DENIED_MORE) { + view.performAccessibilityAction( + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); + } + mResultListener.onPermissionGrantResult(mGroupName, affectedForegroundPermissions, + resultValue); + } + } + + private Map<Integer, Integer> initializeButtonToResultValueMap() { + Map<Integer, Integer> map = new HashMap<>(); + map.put(ALLOW_ALL_BUTTON, GRANTED_ALWAYS); + map.put(ALLOW_BUTTON, GRANTED_ALWAYS); + map.put(ALLOW_FOREGROUND_BUTTON, GRANTED_FOREGROUND_ONLY); + map.put(ALLOW_ALWAYS_BUTTON, GRANTED_ALWAYS); + map.put(ALLOW_ONE_TIME_BUTTON, GRANTED_ONE_TIME); + map.put(ALLOW_SELECTED_BUTTON, GRANTED_USER_SELECTED); + map.put(DONT_ALLOW_MORE_SELECTED_BUTTON, DENIED_MORE); + map.put(DENY_BUTTON, DENIED); + map.put(NO_UPGRADE_BUTTON, DENIED); + map.put(NO_UPGRADE_OT_BUTTON, DENIED); + map.put(DENY_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN); + map.put(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN); + map.put(NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN); + return map; + } + + private void generateResIdToButtonMap() { + mButtonResIdToNum.put(R.id.permission_allow_button, ALLOW_BUTTON); + mButtonResIdToNum.put( + R.id.permission_allow_foreground_only_button, + ALLOW_FOREGROUND_BUTTON + ); + mButtonResIdToNum.put(R.id.permission_deny_button, DENY_BUTTON); + mButtonResIdToNum.put( + R.id.permission_deny_and_dont_ask_again_button, + DENY_AND_DONT_ASK_AGAIN_BUTTON + ); + mButtonResIdToNum.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON); + mButtonResIdToNum.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON); + mButtonResIdToNum.put( + R.id.permission_no_upgrade_and_dont_ask_again_button, + NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON + ); + mButtonResIdToNum.put( + R.id.permission_no_upgrade_one_time_button, + NO_UPGRADE_OT_BUTTON + ); + mButtonResIdToNum.put( + R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, + NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON + ); + mButtonResIdToNum.put(R.id.permission_allow_all_button, ALLOW_ALL_BUTTON); + mButtonResIdToNum.put(R.id.permission_allow_selected_button, ALLOW_SELECTED_BUTTON); + mButtonResIdToNum.put( + R.id.permission_dont_allow_more_selected_button, + DONT_ALLOW_MORE_SELECTED_BUTTON + ); + + mLocationResIdToNum.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT); + mLocationResIdToNum.put( + R.id.permission_location_accuracy_radio_fine, + FINE_RADIO_BUTTON + ); + mLocationResIdToNum.put( + R.id.permission_location_accuracy_radio_coarse, + COARSE_RADIO_BUTTON + ); + mLocationResIdToNum.put( + R.id.permission_location_accuracy_radio_group, + DIALOG_WITH_BOTH_LOCATIONS + ); + mLocationResIdToNum.put( + R.id.permission_location_accuracy_fine_only, + DIALOG_WITH_FINE_LOCATION_ONLY + ); + mLocationResIdToNum.put( + R.id.permission_location_accuracy_coarse_only, + DIALOG_WITH_COARSE_LOCATION_ONLY + ); + } +} 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 9f2e54b2f..300c108f9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java @@ -30,6 +30,7 @@ import android.app.AlertDialog; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.view.WindowManager; @@ -37,6 +38,7 @@ import com.android.car.ui.AlertDialogBuilder; import com.android.car.ui.recyclerview.CarUiContentListItem; import com.android.car.ui.recyclerview.CarUiListItem; import com.android.car.ui.recyclerview.CarUiListItemAdapter; +import com.android.permission.flags.Flags; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; @@ -44,6 +46,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; + /** * A {@link GrantPermissionsViewHandler} that is specific for the auto use-case. In this case, the * permissions dialog is displayed using car-ui-lib {@link AlertDialogBuilder} @@ -55,11 +58,16 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand private static final String ARG_GROUP_ICON = "ARG_GROUP_ICON"; private static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE"; private static final String ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE"; - private static final String ARG_BUTTON_VISIBILITIES = "ARG_BUTTON_VISIBILITIES"; + private static final String ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES"; + private static final String ARG_DIALOG_LOCATION_VISIBILITIES = + "ARG_DIALOG_LOCATION_VISIBILITIES"; + private static final String ARG_DIALOG_SELECTED_PRECISION = "ARG_DIALOG_SELECTED_PRECISION"; + private static final String LOG_TAG = GrantPermissionsAutoViewHandler.class.getSimpleName(); private final Context mContext; private ResultListener mResultListener; private AlertDialog mDialog; + private AutoLocationPermissionPromptView mAutoLocationPermissionPromptView; private String mGroupName; private int mGroupCount; private int mGroupIndex; @@ -68,20 +76,34 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand private CharSequence mDetailMessage; private boolean[] mButtonVisibilities; + // Tracks the selected location accuracy option. + // States: 0 (no option), 1 (fine), 2 (coarse) + private int mSelectedPrecision = 0; + + public GrantPermissionsAutoViewHandler(Context context, String appPackageName) { mContext = context; + mAutoLocationPermissionPromptView = new AutoLocationPermissionPromptView(context); } + @Override public GrantPermissionsViewHandler setResultListener(ResultListener listener) { mResultListener = listener; + mAutoLocationPermissionPromptView.setResultListener(listener); return this; } @Override public View createView() { - // We will use a system dialog instead of a locally defined view. - return new View(mContext); + Log.d(LOG_TAG, "enableCoarseFineLocationPromptForAaos flag set to: " + + Flags.enableCoarseFineLocationPromptForAaos()); + if (Flags.enableCoarseFineLocationPromptForAaos()) { + return mAutoLocationPermissionPromptView.createView(); + } else { + // We will use a system dialog instead of a locally defined view. + return new View(mContext); + } } @Override @@ -104,7 +126,13 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand mDetailMessage = detailMessage; setButtonVisibilities(buttonVisibilities); - update(); + if (Flags.enableCoarseFineLocationPromptForAaos()) { + mAutoLocationPermissionPromptView.updateState(groupName, + icon, message, detailMessage, buttonVisibilities, locationVisibilities, + mSelectedPrecision); + } else { + update(); + } } private void update() { @@ -192,7 +220,8 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon); arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage); arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, mDetailMessage); - arguments.putBooleanArray(ARG_BUTTON_VISIBILITIES, mButtonVisibilities); + arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, mButtonVisibilities); + arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, mSelectedPrecision); } @Override @@ -203,17 +232,30 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT); mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX); mDetailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE); - setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_BUTTON_VISIBILITIES)); + setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES)); + boolean[] locationVisibilities = + savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES); + mSelectedPrecision = savedInstanceState.getInt(ARG_DIALOG_SELECTED_PRECISION); - update(); + if (Flags.enableCoarseFineLocationPromptForAaos()) { + mAutoLocationPermissionPromptView.updateState(mGroupName, + mGroupIcon, mGroupMessage, mDetailMessage, + mButtonVisibilities, locationVisibilities, mSelectedPrecision); + } else { + update(); + } } + private void setButtonVisibilities(boolean[] visibilities) { // If GrantPermissionsActivity sent the user directly to settings, button visibilities are // not created. If the activity was then destroyed by the system, once the activity is // recreated to perform onActivityResult, it will try to loadInstanceState in onCreate but // the button visibilities were never set, so they will be null. mButtonVisibilities = visibilities == null ? new boolean[0] : visibilities; + if (visibilities == null) { + Log.e(LOG_TAG, "Button visibilities are null"); + } } @Override 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 08460178c..36597a3a3 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 @@ -23,30 +23,30 @@ import androidx.annotation.RequiresApi import androidx.preference.Preference.OnPreferenceClickListener import com.android.car.ui.preference.CarUiPreference import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo /** Preference that displays a permission usage for an app. */ @RequiresApi(Build.VERSION_CODES.S) class AutoPermissionHistoryPreference( context: Context, - historyPreferenceData: PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData + historyPreferenceData: AppPermissionAccessUiInfo, ) : CarUiPreference(context) { init { - title = historyPreferenceData.preferenceTitle + title = historyPreferenceData.packageLabel summary = if (historyPreferenceData.summaryText != null) { context.getString( R.string.auto_permission_usage_timeline_summary, DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime), - historyPreferenceData.summaryText + historyPreferenceData.summaryText, ) } else { DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime) } - if (historyPreferenceData.appIcon != null) { - icon = historyPreferenceData.appIcon + if (historyPreferenceData.badgedPackageIcon != null) { + icon = historyPreferenceData.badgedPackageIcon } onPreferenceClickListener = OnPreferenceClickListener { @@ -56,12 +56,12 @@ class AutoPermissionHistoryPreference( PermissionUsageDetailsViewModel.createHistoryPreferenceClickIntent( context = context, userHandle = historyPreferenceData.userHandle, - packageName = historyPreferenceData.pkgName, + packageName = historyPreferenceData.packageName, permissionGroup = historyPreferenceData.permissionGroup, accessEndTime = historyPreferenceData.accessEndTime, accessStartTime = historyPreferenceData.accessStartTime, showingAttribution = historyPreferenceData.showingAttribution, - attributionTags = historyPreferenceData.attributionTags + attributionTags = historyPreferenceData.attributionTags.toSet(), ) ) 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 481543eb6..8edd39913 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 @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("DEPRECATION") - package com.android.permissioncontroller.permission.ui.auto.dashboard -import android.app.role.RoleManager import android.content.Intent import android.os.Build import android.os.Bundle @@ -36,16 +33,12 @@ 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.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.auto.AutoDividerPreference -import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelFactoryLegacy -import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy +import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel -import com.android.permissioncontroller.permission.utils.Utils import java.time.Clock import java.time.Instant import java.time.ZoneId @@ -54,9 +47,7 @@ import java.time.temporal.ChronoUnit import java.util.concurrent.atomic.AtomicReference @RequiresApi(Build.VERSION_CODES.S) -class AutoPermissionUsageDetailsFragment : - AutoSettingsFrameFragment(), PermissionsUsagesChangeCallback { - +class AutoPermissionUsageDetailsFragment : AutoSettingsFrameFragment() { companion object { private const val LOG_TAG = "AutoPermissionUsageDetailsFragment" private const val KEY_SESSION_ID = "_session_id" @@ -70,14 +61,11 @@ class AutoPermissionUsageDetailsFragment : .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]. */ fun newInstance( groupName: String?, showSystem: Boolean, - sessionId: Long + sessionId: Long, ): AutoPermissionUsageDetailsFragment { return AutoPermissionUsageDetailsFragment().apply { arguments = @@ -92,14 +80,10 @@ class AutoPermissionUsageDetailsFragment : private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID) - private lateinit var permissionUsages: PermissionUsages - private lateinit var usageViewModel: PermissionUsageDetailsViewModelLegacy + private lateinit var usageViewModel: BasePermissionUsageDetailsViewModel private lateinit var filterGroup: String - private lateinit var roleManager: RoleManager - private var appPermissionUsages: List<AppPermissionUsage> = listOf() private var showSystem = false - private var finishedInitialLoad = false private var hasSystemApps = false /** Unique Id of a request */ @@ -116,7 +100,7 @@ class AutoPermissionUsageDetailsFragment : !requireArguments().containsKey(Intent.EXTRA_PERMISSION_GROUP_NAME) or (requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME) == null) ) { - DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_USER}") + DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_PERMISSION_GROUP_NAME}") activity?.finish() return } @@ -130,28 +114,21 @@ class AutoPermissionUsageDetailsFragment : headerLabel = resources.getString( R.string.permission_group_usage_title, - getPermGroupLabel(requireContext(), filterGroup) + getPermGroupLabel(requireContext(), filterGroup), ) - - val context = preferenceManager.getContext() - permissionUsages = PermissionUsages(context) - roleManager = Utils.getSystemServiceSafe(context, RoleManager::class.java) - val usageViewModelFactory = - PermissionUsageDetailsViewModelFactoryLegacy( + val factory = + PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory( PermissionControllerApplication.get(), - roleManager, + this, filterGroup, - sessionId ) usageViewModel = - ViewModelProvider(this, usageViewModelFactory)[ - PermissionUsageDetailsViewModelLegacy::class.java] - - reloadData() + ViewModelProvider(this, factory)[BasePermissionUsageDetailsViewModel::class.java] + usageViewModel.getPermissionUsagesDetailsInfoUiLiveData().observe(this, this::updateUI) } override fun onCreatePreferences(bundlle: Bundle?, s: String?) { - preferenceScreen = preferenceManager.createPreferenceScreen(context!!) + preferenceScreen = preferenceManager.createPreferenceScreen(requireContext()) } private fun setupHeaderPreferences() { @@ -161,38 +138,16 @@ class AutoPermissionUsageDetailsFragment : preferenceScreen.addPreference(AutoDividerPreference(context)) } - /** Reloads the data to show. */ - private fun reloadData() { - usageViewModel.loadPermissionUsages( - requireActivity().getLoaderManager(), - permissionUsages, - this, - FILTER_24_HOURS - ) - if (finishedInitialLoad) { - setLoading(true) - } - } - - override fun onPermissionUsagesChanged() { - if (permissionUsages.usages.isEmpty()) { - return - } - appPermissionUsages = ArrayList(permissionUsages.usages) - updateUI() - } - private fun updateSystemToggle() { if (!showSystem) { PermissionControllerStatsLog.write( PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED, ) } showSystem = !showSystem updateAction() - updateUI() } private fun updateAction() { @@ -206,47 +161,36 @@ class AutoPermissionUsageDetailsFragment : } else { getString(R.string.menu_show_system) } - setAction(label) { updateSystemToggle() } + setAction(label) { + usageViewModel.updateShowSystemAppsToggle(!showSystem) + updateSystemToggle() + } } - private fun updateUI() { - if (appPermissionUsages.isEmpty()) { + private fun updateUI(uiInfo: PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState) { + if ( + activity == null || + uiInfo is PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState.Loading + ) { return } preferenceScreen.removeAll() setupHeaderPreferences() - - val uiData = - usageViewModel.buildPermissionUsageDetailsUiData( - appPermissionUsages, - showSystem, - SHOW_7_DAYS - ) - - if (hasSystemApps != uiData.shouldDisplayShowSystemToggle) { - hasSystemApps = uiData.shouldDisplayShowSystemToggle + val uiData = uiInfo as PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState.Success + if (hasSystemApps != uiData.containsSystemAppUsage) { + hasSystemApps = uiData.containsSystemAppUsage updateAction() } - val category = AtomicReference(PreferenceCategory(requireContext())) preferenceScreen.addPreference(category.get()) - AppDataLoader(context) { - renderHistoryPreferences( - uiData.getHistoryPreferenceDataList(), - category, - preferenceScreen - ) + renderHistoryPreferences(uiData.appPermissionAccessUiInfoList, category, preferenceScreen) - setLoading(false) - finishedInitialLoad = true - permissionUsages.stopLoader(requireActivity().getLoaderManager()) - } - .execute(*uiData.permissionApps.toTypedArray()) + setLoading(false) } fun createPermissionHistoryPreference( - historyPreferenceData: PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData + historyPreferenceData: AppPermissionAccessUiInfo ): Preference { return AutoPermissionHistoryPreference(requireContext(), historyPreferenceData) } @@ -257,7 +201,7 @@ class AutoPermissionUsageDetailsFragment : summary = getString( R.string.permission_group_usage_subtitle_24h, - getPermGroupLabel(requireContext(), filterGroup) + getPermGroupLabel(requireContext(), filterGroup), ) isSelectable = false } @@ -271,7 +215,7 @@ class AutoPermissionUsageDetailsFragment : summary = getString( R.string.manage_permission_summary, - getPermGroupLabel(requireContext(), filterGroup) + getPermGroupLabel(requireContext(), filterGroup), ) onPreferenceClickListener = Preference.OnPreferenceClickListener { @@ -287,9 +231,8 @@ class AutoPermissionUsageDetailsFragment : } /** Render the provided [historyPreferenceDataList] into the [preferenceScreen] UI. */ - fun renderHistoryPreferences( - historyPreferenceDataList: - List<PermissionUsageDetailsViewModelLegacy.HistoryPreferenceData>, + private fun renderHistoryPreferences( + historyPreferenceDataList: List<AppPermissionAccessUiInfo>, category: AtomicReference<PreferenceCategory>, preferenceScreen: PreferenceScreen, ) { @@ -299,7 +242,7 @@ class AutoPermissionUsageDetailsFragment : val currentDateMs = ZonedDateTime.ofInstant( Instant.ofEpochMilli(usageTimestamp), - Clock.system(ZoneId.systemDefault()).zone + Clock.system(ZoneId.systemDefault()).zone, ) .truncatedTo(ChronoUnit.DAYS) .toEpochSecond() * 1000L 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 c11129514..f52eaadcd 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 @@ -13,16 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("DEPRECATION") - package com.android.permissioncontroller.permission.ui.auto.dashboard -import android.app.Activity -import android.app.Application -import android.app.role.RoleManager -import android.content.Context +import android.Manifest import android.os.Build import android.os.Bundle +import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModelProvider import com.android.car.ui.preference.CarUiPreference @@ -32,45 +28,37 @@ 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.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.legacy.PermissionUsageViewModelFactoryLegacy -import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy -import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy.PermissionGroupWithUsageCount -import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils -import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel +import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModelFactory +import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState @RequiresApi(Build.VERSION_CODES.S) -class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsagesChangeCallback { +class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { companion object { + private val LOG_TAG = AutoPermissionUsageFragment::class.simpleName private const val KEY_SESSION_ID = "_session_id" + private val PERMISSION_GROUP_ORDER: Map<String, Int> = + java.util.Map.of( + Manifest.permission_group.LOCATION, + 0, + Manifest.permission_group.CAMERA, + 1, + Manifest.permission_group.MICROPHONE, + 2, + ) + private const val DEFAULT_ORDER: Int = 3 } private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID) - private lateinit var permissionUsages: PermissionUsages - private lateinit var usageViewModel: PermissionUsageViewModelLegacy - private lateinit var managePermissionsViewModel: ManagePermissionsViewModel - - private var appPermissionUsages: List<AppPermissionUsage> = listOf() - private var permissionGroups: List<PermGroupPackagesUiInfo> = listOf() private var showSystem = false - - // Auto currently doesn't show last 7 days due to the UX constraint that there is no pattern to - // support multiple actions (showSystem & show7Days). Support will likely be added once this - // pattern is resolved. - private val show7Days = false - private var finishedInitialLoad = false private var hasSystemApps = false /** Unique Id of a request */ private var sessionId: Long = 0 + private lateinit var mViewModel: PermissionUsageViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -80,26 +68,10 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag ?: (arguments?.getLong(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID) ?: Constants.INVALID_SESSION_ID) - val context: Context = preferenceManager.getContext() - 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 usageViewModelFactory = PermissionUsageViewModelFactoryLegacy(roleManager) - usageViewModel = - ViewModelProvider(this, usageViewModelFactory)[ - PermissionUsageViewModelLegacy::class.java] - - managePermissionsViewModel.standardPermGroupsLiveData.observe( - this, - this::onPermissionGroupsChanged - ) + val factory = PermissionUsageViewModelFactory(requireActivity().application) + mViewModel = ViewModelProvider(this, factory)[PermissionUsageViewModel::class.java] + mViewModel.permissionUsagesUiLiveData.observe(this, this::updateAllUI) setLoading(true) - reloadData() } override fun onSaveInstanceState(outState: Bundle) { @@ -107,13 +79,8 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag outState.putLong(SESSION_ID_KEY, sessionId) } - private fun onPermissionGroupsChanged(permissionGroups: List<PermGroupPackagesUiInfo>) { - this.permissionGroups = permissionGroups - updateUI() - } - override fun onCreatePreferences(bundlle: Bundle?, s: String?) { - preferenceScreen = preferenceManager.createPreferenceScreen(context!!) + preferenceScreen = preferenceManager.createPreferenceScreen(requireContext()) } private fun updateSystemToggle() { @@ -121,14 +88,14 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag PermissionControllerStatsLog.write( PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED, ) } showSystem = !showSystem updateAction() - updateUI() } + /** Creates Show/Hide system button if necessary. */ private fun updateAction() { if (!hasSystemApps) { setAction(null, null) @@ -140,100 +107,55 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag } else { getString(R.string.menu_show_system) } - setAction(label) { updateSystemToggle() } - } - - /** Reloads the data to show. */ - private fun reloadData() { - usageViewModel.loadPermissionUsages( - requireActivity().getLoaderManager(), - permissionUsages, - this - ) - if (finishedInitialLoad) { - setLoading(false) + setAction(label) { + mViewModel.updateShowSystem(!showSystem) + updateSystemToggle() } } - override fun onPermissionUsagesChanged() { - if (permissionUsages.usages.isEmpty()) { - return - } - appPermissionUsages = ArrayList(permissionUsages.usages) - updateUI() - } - - private fun updateUI() { - if (permissionGroups.isEmpty() || appPermissionUsages.isEmpty()) { + private fun updateAllUI(uiData: PermissionUsagesUiState) { + Log.v(LOG_TAG, "Privacy dashboard data = $uiData") + if (activity == null || uiData is PermissionUsagesUiState.Loading) { return } getPreferenceScreen().removeAll() - - val permissionUsagesUiData = - usageViewModel.buildPermissionUsagesUiData( - appPermissionUsages, - show7Days, - showSystem, - requireContext() - ) - val permissionApps = permissionUsagesUiData.permissionApps - val displayShowSystemToggle = permissionUsagesUiData.displayShowSystemToggle - - if (hasSystemApps != displayShowSystemToggle) { - hasSystemApps = displayShowSystemToggle + val permissionUsagesUiData = uiData as PermissionUsagesUiState.Success + if (hasSystemApps != permissionUsagesUiData.containsSystemAppUsage) { + hasSystemApps = permissionUsagesUiData.containsSystemAppUsage updateAction() } - val permissionGroupWithUsageCounts: List<PermissionGroupWithUsageCount> = - permissionUsagesUiData.orderedPermissionGroupsWithUsageCount - - addUIContent(permissionGroupWithUsageCounts, permissionApps) - } + val permissionGroupWithUsageCounts = permissionUsagesUiData.permissionGroupUsageCount + val permissionGroupWithUsageCountsEntries = permissionGroupWithUsageCounts.entries.toList() - /** Use the usages and permApps that are previously constructed to add UI content to the page */ - private fun addUIContent( - 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) - } - .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 + permissionGroupWithUsageCountsEntries.sortedWith( + Comparator.comparing { permissionGroupWithUsageCount: Map.Entry<String, Int> -> + PERMISSION_GROUP_ORDER.getOrDefault( + permissionGroupWithUsageCount.key, + DEFAULT_ORDER, ) - getPreferenceScreen().addPreference(permissionUsagePreference) } - finishedInitialLoad = true - setLoading(false) - val activity: Activity? = activity - if (activity != null) { - permissionUsages.stopLoader(activity.loaderManager) + .thenComparing { permissionGroupWithUsageCount: Map.Entry<String, Int> -> + mViewModel.getPermissionGroupLabel( + requireContext(), + permissionGroupWithUsageCount.key, + ) } - } - .execute(*permApps.toTypedArray()) + ) + + for (i in permissionGroupWithUsageCountsEntries.indices) { + val permissionUsagePreference = CarUiPreference(requireContext()) + PermissionUsageControlPreferenceUtils.initPreference( + permissionUsagePreference, + requireContext(), + permissionGroupWithUsageCountsEntries[i].key, + permissionGroupWithUsageCountsEntries[i].value, + showSystem, + sessionId, + false, + ) + getPreferenceScreen().addPreference(permissionUsagePreference) + } + setLoading(false) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsFragment.java index a09312a60..81de139e4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsFragment.java @@ -36,7 +36,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; -import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceGroup; import com.android.permissioncontroller.R; @@ -229,7 +228,7 @@ public final class AllAppPermissionsFragment extends SettingsWithLargeHeader { } PreferenceGroup pref = findPreference(groupName); if (pref == null) { - pref = new PreferenceCategory(getPreferenceManager().getContext()); + pref = new PermissionPreferenceCategory(getPreferenceManager().getContext()); pref.setKey(groupName); pref.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), groupName)); getPreferenceScreen().addPreference(pref); @@ -251,7 +250,7 @@ public final class AllAppPermissionsFragment extends SettingsWithLargeHeader { getActivity().getApplication(), mPackageName, groupName, mUser, false); pref = new MyMultiTargetSwitchPreference(context, permName, appPermGroup); } else { - pref = new Preference(context); + pref = new PermissionPreference(context); } pref.setIcon(KotlinUtils.INSTANCE.getPermInfoIcon(context, permName)); pref.setTitle(KotlinUtils.INSTANCE.getPermInfoLabel(context, permName)); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsWrapperFragment.java index 354081cb5..84f1b8b2b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AllAppPermissionsWrapperFragment.java @@ -43,4 +43,9 @@ public class AllAppPermissionsWrapperFragment extends PermissionsCollapsingToolb packageName, filterGroup, userHandle)); return instance; } + + @Override + protected boolean isPermissionSettings() { + return true; + } } 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 eff5738fc..969ac17eb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java @@ -79,7 +79,6 @@ import com.android.permissioncontroller.permission.utils.StringUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils; import com.android.settingslib.HelpUtils; -import com.android.settingslib.widget.FooterPreference; import java.text.Collator; import java.time.Instant; @@ -185,9 +184,8 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i Context context = getPreferenceManager().getContext(); mPermissionUsages = new PermissionUsages(context); - long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled() - ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 : - AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1; + long aggregateDataFilterBeginDays = + AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7; long filterTimeBeginMillis = Math.max(System.currentTimeMillis() - DAYS.toMillis(aggregateDataFilterBeginDays), @@ -252,6 +250,12 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i mPackageName, mUser)); return true; } + + case MENU_ALLOW_RESTRICTED_SETTINGS: { + mViewModel.clearRestriction(); + getActivity().invalidateOptionsMenu(); + return true; + } } return super.onOptionsItemSelected(item); } @@ -266,6 +270,20 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i getClass().getName()); } } + + if (SdkLevel.isAtLeastT() && !SdkLevel.isAtLeastV() + && Flags.enhancedConfirmationBackportEnabled()) { + menu.add(Menu.NONE, MENU_ALLOW_RESTRICTED_SETTINGS, Menu.NONE, + R.string.allow_restricted_settings); + } + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + final MenuItem allowRestrictedSettingsMenu = menu.findItem(MENU_ALLOW_RESTRICTED_SETTINGS); + if (allowRestrictedSettingsMenu != null) { + allowRestrictedSettingsMenu.setVisible(mViewModel.isClearRestrictedAllowed()); + } } private static void bindUi(SettingsWithLargeHeader fragment, String packageName, @@ -432,11 +450,11 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i private void addAutoRevokePreferences(PreferenceScreen screen) { Context context = screen.getPreferenceManager().getContext(); - PreferenceCategory autoRevokeCategory = new PreferenceCategory(context); + PreferenceCategory autoRevokeCategory = new PermissionPreferenceCategory(context); autoRevokeCategory.setKey(AUTO_REVOKE_CATEGORY_KEY); screen.addPreference(autoRevokeCategory); - SwitchPreference autoRevokeSwitch = new SwitchPreference(context); + SwitchPreference autoRevokeSwitch = new PermissionSwitchPreference(context); autoRevokeSwitch.setOnPreferenceClickListener((preference) -> { mViewModel.setAutoRevoke(autoRevokeSwitch.isChecked()); return true; @@ -459,8 +477,9 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i autoRevokeSwitch.setKey(AUTO_REVOKE_SWITCH_KEY); autoRevokeCategory.addPreference(autoRevokeSwitch); - Preference autoRevokeSummary = SdkLevel.isAtLeastS() ? new FooterPreference(context) - : new Preference(context); + Preference autoRevokeSummary = + SdkLevel.isAtLeastS() ? new PermissionFooterPreference(context) + : new PermissionPreference(context); autoRevokeSummary.setIcon(Utils.applyTint(getActivity(), R.drawable.ic_info_outline, android.R.attr.colorControlNormal)); autoRevokeSummary.setKey(AUTO_REVOKE_SUMMARY_KEY); @@ -503,7 +522,10 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i } groupLabels.sort(mCollator); - if (groupLabels.isEmpty()) { + autoRevokeSummary.setVisible(true); + if (state.isExemptBySystem()) { + autoRevokeSummary.setVisible(false); + } else if (groupLabels.isEmpty()) { autoRevokeSummary.setSummary(R.string.auto_revoke_summary); } else { autoRevokeSummary.setSummary(getString(R.string.auto_revoke_summary_with_permissions, @@ -523,7 +545,7 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i } private Preference setUpCustomPermissionsScreen(Context context, int count, String category) { - final Preference extraPerms = new Preference(context); + final Preference extraPerms = new PermissionPreference(context); extraPerms.setIcon(Utils.applyTint(getActivity(), R.drawable.ic_toc, android.R.attr.colorControlNormal)); extraPerms.setTitle(R.string.additional_permissions); @@ -540,7 +562,7 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i private void setNoPermissionPreference(PreferenceCategory category, @StringRes int stringId, Context context) { - Preference empty = new Preference(context); + Preference empty = new PermissionPreference(context); empty.setKey(getString(stringId)); empty.setTitle(empty.getKey()); empty.setSelectable(false); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsWrapperFragment.java index 5d7a90106..951ade811 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsWrapperFragment.java @@ -29,4 +29,9 @@ public class AppPermissionGroupsWrapperFragment extends PermissionsCollapsingToo public PreferenceFragmentCompat createPreferenceFragment() { return new AppPermissionGroupsFragment(); } + + @Override + protected boolean isPermissionSettings() { + return true; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java index 4655d9afb..080c7cfdc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java @@ -19,6 +19,8 @@ package com.android.permissioncontroller.permission.ui.handheld; import androidx.annotation.NonNull; import androidx.preference.PreferenceFragmentCompat; +import com.android.permissioncontroller.permission.compat.AppPermissionFragmentCompat; + /** * Wrapper over AppPermissionFragment */ @@ -27,6 +29,11 @@ public class AppPermissionWrapperFragment extends PermissionsCollapsingToolbarBa @NonNull @Override public PreferenceFragmentCompat createPreferenceFragment() { - return new AppPermissionFragment(); + return AppPermissionFragmentCompat.createFragment(getContext()); + } + + @Override + protected boolean isPermissionSettings() { + return true; } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/BasePermissionReviewPreference.java index 1089fcac2..a763d5f92 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/BasePermissionReviewPreference.java @@ -48,7 +48,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; /** * A preference for representing a permission group requested by an app. */ -class PermissionPreference extends MultiTargetSwitchPreference { +class BasePermissionReviewPreference extends MultiTargetSwitchPreference { /** * holds state for the permission group represented by this preference. @@ -114,7 +114,7 @@ class PermissionPreference extends MultiTargetSwitchPreference { void onBackgroundAccessChosen(String key, int chosenItem); } - PermissionPreference(PreferenceFragmentCompat fragment, LightAppPermGroup group, + BasePermissionReviewPreference(PreferenceFragmentCompat fragment, LightAppPermGroup group, PermissionPreferenceChangeListener callbacks, ReviewPermissionsViewModel reviewPermissionsViewModel) { super(fragment.getPreferenceManager().getContext()); @@ -417,7 +417,7 @@ class PermissionPreference extends MultiTargetSwitchPreference { * <li>{@code showDefaultDenyDialog}</li> * <li>{@link DefaultDenyDialog#onCreateDialog}</li> * <li>{@link PermissionPreferenceOwnerFragment#onDenyAnyWay}</li> - * <li>{@link PermissionPreference#onDenyAnyWay}</li> + * <li>{@link BasePermissionReviewPreference#onDenyAnyWay}</li> * </ol> * * @param changeTarget Whether background or foreground should be changed @@ -448,7 +448,7 @@ class PermissionPreference extends MultiTargetSwitchPreference { * <li>{@code showBackgroundChooserDialog}</li> * <li>{@link BackgroundAccessChooser#onCreateDialog}</li> * <li>{@link PermissionPreferenceOwnerFragment#onBackgroundAccessChosen}</li> - * <li>{@link PermissionPreference#onBackgroundAccessChosen}</li> + * <li>{@link BasePermissionReviewPreference#onBackgroundAccessChosen}</li> * </ol> */ private void showBackgroundChooserDialog() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt index 37ac50bb8..030b12c66 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt @@ -77,10 +77,10 @@ class HandheldUnusedAppsFragment : override fun createFooterPreference(): Preference { var preference: Preference if (isHibernationEnabled()) { - preference = com.android.settingslib.widget.FooterPreference(requireContext()) + preference = PermissionFooterPreference(requireContext()) preference.summary = getString(R.string.unused_apps_page_summary) } else { - preference = FooterPreference(requireContext()) + preference = UnusedAppsFooterPreference(requireContext()) preference.summary = getString(R.string.auto_revoked_apps_page_summary) preference.secondSummary = getString(R.string.auto_revoke_open_app_message) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java index 35236b8de..dd460aa2f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsFragment.java @@ -24,6 +24,8 @@ import android.view.MenuItem; import androidx.lifecycle.ViewModelProvider; +import com.android.permission.flags.Flags; +import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData; import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModel; import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModelFactory; @@ -48,6 +50,14 @@ public class ManageCustomPermissionsFragment extends ManagePermissionsFragment { return arguments; } + private PermGroupsPackagesUiInfoLiveData getPermGroupsLiveData() { + if (Flags.declutteredPermissionManagerEnabled()) { + return mViewModel.getAdditionaPermGroupsUiInfo(); + } else { + return mViewModel.getUiDataLiveData(); + } + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -56,9 +66,9 @@ public class ManageCustomPermissionsFragment extends ManagePermissionsFragment { new ManageCustomPermissionsViewModelFactory(getActivity().getApplication()); mViewModel = new ViewModelProvider(this, factory) .get(ManageCustomPermissionsViewModel.class); - mPermissionGroups = mViewModel.getUiDataLiveData().getValue(); + mPermissionGroups = getPermGroupsLiveData().getValue(); - mViewModel.getUiDataLiveData().observe(this, permissionGroups -> { + getPermGroupsLiveData().observe(this, permissionGroups -> { if (permissionGroups == null) { mPermissionGroups = new HashMap<>(); } else { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsWrapperFragment.java index f7409b97f..c96fb6a4a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageCustomPermissionsWrapperFragment.java @@ -30,4 +30,9 @@ public class ManageCustomPermissionsWrapperFragment public PreferenceFragmentCompat createPreferenceFragment() { return new ManageCustomPermissionsFragment(); } + + @Override + protected boolean isPermissionSettings() { + return true; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManagePermissionsFragment.java index 47f4a4c56..15b76e592 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManagePermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManagePermissionsFragment.java @@ -158,7 +158,7 @@ abstract class ManagePermissionsFragment extends PermissionsFrameFragment * A preference whose icons have the same fixed size. Allows the setting of dividers above and * below the preference. */ - protected static final class FixedSizeIconPreference extends Preference { + protected static final class FixedSizeIconPreference extends PermissionPreference { private boolean mShowDividerAbove = true; private boolean mShowDividerBelow = false; @@ -178,7 +178,7 @@ abstract class ManagePermissionsFragment extends PermissionsFrameFragment ImageView icon = ((ImageView) holder.findViewById(android.R.id.icon)); icon.setAdjustViewBounds(true); int size = getContext().getResources().getDimensionPixelSize( - R.dimen.permission_icon_size); + R.dimen.permission_preference_permission_group_icon_size); icon.setMaxWidth(size); icon.setMaxHeight(size); icon.getLayoutParams().width = size; 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 86fcf2c27..51c0906a2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java @@ -31,12 +31,13 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData; import com.android.permissioncontroller.permission.ui.UnusedAppsFragment; import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel; import com.android.permissioncontroller.permission.utils.StringUtils; import com.android.permissioncontroller.permission.utils.Utils; -import com.android.settingslib.widget.FooterPreference; /** * Fragment that allows the user to manage standard permissions. @@ -59,6 +60,14 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr return arguments; } + private PermGroupsPackagesUiInfoLiveData getPermGroupsLiveData() { + if (Flags.declutteredPermissionManagerEnabled()) { + return mViewModel.getUsedStandardPermGroupsUiInfo(); + } else { + return mViewModel.getUiDataLiveData(); + } + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -66,12 +75,12 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr final Application application = getActivity().getApplication(); mViewModel = new ViewModelProvider(this, AndroidViewModelFactory.getInstance(application)) .get(ManageStandardPermissionsViewModel.class); - mPermissionGroups = mViewModel.getUiDataLiveData().getValue(); + mPermissionGroups = getPermGroupsLiveData().getValue(); - mViewModel.getUiDataLiveData().observe(this, permissionGroups -> { + getPermGroupsLiveData().observe(this, permissionGroups -> { // Once we have loaded data for the first time, further loads should be staggered, // for performance reasons. - mViewModel.getUiDataLiveData().setLoadStaggered(true); + getPermGroupsLiveData().setLoadStaggered(true); if (permissionGroups != null) { mPermissionGroups = permissionGroups; updatePermissionsUi(); @@ -81,13 +90,18 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr } // If we've loaded all LiveDatas, no need to prioritize loading any particular one - if (!mViewModel.getUiDataLiveData().isStale()) { - mViewModel.getUiDataLiveData().setFirstLoadGroup(null); + if (!getPermGroupsLiveData().isStale()) { + getPermGroupsLiveData().setFirstLoadGroup(null); } }); mViewModel.getNumCustomPermGroups().observe(this, permNames -> updatePermissionsUi()); mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi()); + if (Flags.declutteredPermissionManagerEnabled()) { + mViewModel.getNumUnusedStandardPermGroups().observe( + this, show -> updatePermissionsUi() + ); + } } @Override @@ -119,6 +133,14 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr if (mViewModel.getNumCustomPermGroups().getValue() != null) { numExtraPermissions = mViewModel.getNumCustomPermGroups().getValue(); } + if (Flags.declutteredPermissionManagerEnabled()) { + if (mViewModel.getNumUnusedStandardPermGroups().getValue() != null) { + // When decluttered permission manager is enabled, unused + // permission groups will also be displayed in the additional + // permissions screen. + numExtraPermissions += mViewModel.getNumUnusedStandardPermGroups().getValue(); + } + } Preference additionalPermissionsPreference = screen.findPreference(EXTRA_PREFS_KEY); if (numExtraPermissions == 0) { @@ -168,8 +190,9 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr return screen; } - private FooterPreference createAutoRevokeFooterPreferenceForSPlus() { - FooterPreference autoRevokePreference = new FooterPreference(getContext()); + private PermissionFooterPreference createAutoRevokeFooterPreferenceForSPlus() { + PermissionFooterPreference autoRevokePreference = + new PermissionFooterPreference(getContext()); autoRevokePreference.setSummary(R.string.auto_revoked_apps_page_summary); autoRevokePreference.setLearnMoreAction(view -> { mViewModel.showAutoRevoke(this, UnusedAppsFragment.createArgs( @@ -198,7 +221,7 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr 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); + getPermGroupsLiveData().setFirstLoadGroup(permissionGroupName); mViewModel.showPermissionApps(this, PermissionAppsFragment.createArgs( permissionGroupName, getArguments().getLong(EXTRA_SESSION_ID))); } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsWrapperFragment.java index a2f9924fa..a4c9150d3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsWrapperFragment.java @@ -30,4 +30,9 @@ public class ManageStandardPermissionsWrapperFragment public PreferenceFragmentCompat createPreferenceFragment() { return new ManageStandardPermissionsFragment(); } + + @Override + protected boolean isPermissionSettings() { + return true; + } } 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 76e52ad94..e7121709c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java @@ -52,6 +52,7 @@ import androidx.preference.PreferenceScreen; import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.compat.AppPermissionFragmentCompat; import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage; import com.android.permissioncontroller.permission.model.v31.PermissionUsages; import com.android.permissioncontroller.permission.ui.Category; @@ -62,7 +63,6 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.HelpUtils; import com.android.settingslib.utils.applications.AppUtils; -import com.android.settingslib.widget.FooterPreference; import kotlin.Pair; import kotlin.Triple; @@ -324,7 +324,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem return; } - FooterPreference preference = new FooterPreference(context); + PermissionFooterPreference preference = new PermissionFooterPreference(context); preference.setKey(STORAGE_FOOTER_PREFERENCE_KEY); preference.setIcon(Utils.applyTint(getActivity(), R.drawable.ic_info_outline, android.R.attr.colorControlNormal)); @@ -419,7 +419,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem // permission, set up the empty preference. if (packages.size() == 0 && (!isStorageAndLessThanT || !grantCategory.equals(ALLOWED))) { - Preference empty = new Preference(context); + Preference empty = new PermissionPreference(context); empty.setSelectable(false); empty.setKey(category.getKey() + KEY_EMPTY); if (grantCategory.equals(ALLOWED)) { @@ -474,9 +474,9 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem packageName, user)); pref.setOnPreferenceClickListener((Preference p) -> { mViewModel.navigateToAppPermission(this, packageName, user, - AppPermissionFragment.createArgs(packageName, null, mPermGroupName, - user, getClass().getName(), sessionId, - grantCategory.getCategoryName())); + AppPermissionFragmentCompat.createArgs(packageName, null, + mPermGroupName, user, getClass().getName(), sessionId, + grantCategory.getCategoryName(), null)); return true; }); pref.setTitleContentDescription(AppUtils.getAppContentDescription(context, @@ -496,7 +496,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem PreferenceCategory full = findPreference(STORAGE_ALLOWED_FULL); PreferenceCategory scoped = findPreference(STORAGE_ALLOWED_SCOPED); if (full.getPreferenceCount() == 0) { - Preference empty = new Preference(context); + Preference empty = new PermissionPreference(context); empty.setSelectable(false); empty.setKey(STORAGE_ALLOWED_FULL + KEY_EMPTY); empty.setTitle(getString(R.string.no_apps_allowed_full)); @@ -504,7 +504,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem } if (scoped.getPreferenceCount() == 0) { - Preference empty = new Preference(context); + Preference empty = new PermissionPreference(context); empty.setSelectable(false); empty.setKey(STORAGE_ALLOWED_FULL + KEY_EMPTY); empty.setTitle(getString(R.string.no_apps_allowed_scoped)); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsWrapperFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsWrapperFragment.java index 36bbe9a20..70525f7c2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsWrapperFragment.java @@ -28,4 +28,9 @@ public class PermissionAppsWrapperFragment extends PermissionsCollapsingToolbarB public PreferenceFragmentCompat createPreferenceFragment() { return new PermissionAppsFragment(); } + + @Override + protected boolean isPermissionSettings() { + return true; + } } 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 039aca39d..e6a8ef6a0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java @@ -20,8 +20,6 @@ import static android.health.connect.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; -import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.PERSISTENT_DEVICE_ID; import static com.android.permissioncontroller.permission.utils.KotlinUtilsKt.navigateSafe; import android.Manifest; @@ -40,10 +38,11 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.navigation.Navigation; -import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.compat.AppPermissionFragmentCompat; import com.android.permissioncontroller.permission.model.AppPermissionGroup; import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog; import com.android.permissioncontroller.permission.utils.LocationUtils; @@ -54,7 +53,7 @@ import java.util.List; /** * A preference that links to the screen where a permission can be toggled. */ -public class PermissionControlPreference extends Preference { +public class PermissionControlPreference extends PermissionPreference { private final @NonNull Context mContext; private @Nullable Drawable mWidgetIcon; private @Nullable String mWidgetIconContentDescription; @@ -176,14 +175,21 @@ public class PermissionControlPreference extends Preference { @Override public void onBindViewHolder(PreferenceViewHolder holder) { + ImageView icon = ((ImageView) holder.findViewById(android.R.id.icon)); if (mUseSmallerIcon) { - ImageView icon = ((ImageView) holder.findViewById(android.R.id.icon)); - icon.setMaxWidth( - mContext.getResources().getDimensionPixelSize( - com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size)); - icon.setMaxHeight( - mContext.getResources().getDimensionPixelSize( - com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size)); + int iconSize = mContext.getResources().getDimensionPixelSize( + com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size); + icon.setMaxWidth(iconSize); + icon.setMaxHeight(iconSize); + } else if (SdkLevel.isAtLeastV()) { + icon.setAdjustViewBounds(true); + int size = getContext().getResources().getDimensionPixelSize( + R.dimen.permission_preference_permission_group_icon_size); + icon.setMaxWidth(size); + icon.setMaxHeight(size); + icon.getLayoutParams().width = size; + icon.getLayoutParams().height = size; + icon.setScaleType(ImageView.ScaleType.FIT_CENTER); } super.onBindViewHolder(holder); @@ -232,14 +238,8 @@ public class PermissionControlPreference extends Preference { 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); - args.putParcelable(Intent.EXTRA_USER, mUser); - args.putString(EXTRA_CALLER_NAME, mCaller); - args.putLong(EXTRA_SESSION_ID, mSessionId); - args.putString(GRANT_CATEGORY, mGranted); - args.putString(PERSISTENT_DEVICE_ID, mPersistentDeviceId); + Bundle args = AppPermissionFragmentCompat.createArgs(mPackageName, null, + mPermGroupName, mUser, mCaller, mSessionId, mGranted, mPersistentDeviceId); navigateSafe(Navigation.findNavController(holder.itemView), R.id.perm_groups_to_app, args); } else { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt new file mode 100644 index 000000000..e7749d827 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.R +import com.android.settingslib.widget.FooterPreference + +class PermissionFooterPreference : FooterPreference { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + init { + if (SdkLevel.isAtLeastV()) { + layoutResource = R.layout.permission_footer_preference + if (context.resources.getBoolean(R.bool.config_permissionFooterPreferenceIconVisible)) { + setIconVisibility(View.VISIBLE) + } else { + setIconVisibility(View.GONE) + } + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt new file mode 100644 index 000000000..010ca28a7 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld + +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes +import androidx.preference.Preference +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.R + +open class PermissionPreference : Preference { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + @StyleRes defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) + + init { + if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(context)) { + layoutResource = R.layout.permission_preference + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt new file mode 100644 index 000000000..ef95c6c5c --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld + +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes +import androidx.preference.PreferenceCategory +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.R + +open class PermissionPreferenceCategory : PreferenceCategory { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + @StyleRes defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) + + init { + if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(context)) { + layoutResource = R.layout.permission_preference_category + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSwitchPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSwitchPreference.kt new file mode 100644 index 000000000..73339f304 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionSwitchPreference.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.SwitchPreference +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.R + +open class PermissionSwitchPreference : SwitchPreference { + constructor(c: Context) : super(c) + + constructor(c: Context, a: AttributeSet) : super(c, a) + + init { + if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(context)) { + layoutResource = R.layout.permission_preference + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java index fde134b4d..1d433f096 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java @@ -23,6 +23,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceFragmentCompat; +import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.R; import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseFragment; /** @@ -60,4 +62,14 @@ public abstract class PermissionsCollapsingToolbarBaseFragment */ @NonNull public abstract PreferenceFragmentCompat createPreferenceFragment(); + + @Override + protected final boolean useCollapsingToolbar() { + return SdkLevel.isAtLeastS() && (!isPermissionSettings() || getResources() + .getBoolean(R.bool.config_useCollapsingToolbarInPermissionSettings)); + } + + protected boolean isPermissionSettings() { + return false; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java index 17e72b413..af204d7d4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java @@ -28,11 +28,15 @@ import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.ui.handheld.v35.SectionPreferenceGroupAdapter; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.widget.ActionBarShadowController; @@ -42,6 +46,7 @@ public abstract class PermissionsFrameFragment extends PreferenceFragmentCompat static final int MENU_ALL_PERMS = Menu.FIRST + 1; public static final int MENU_SHOW_SYSTEM = Menu.FIRST + 2; public static final int MENU_HIDE_SYSTEM = Menu.FIRST + 3; + static final int MENU_ALLOW_RESTRICTED_SETTINGS = Menu.FIRST + 4; private ViewGroup mPreferencesContainer; @@ -117,6 +122,15 @@ public abstract class PermissionsFrameFragment extends PreferenceFragmentCompat // empty } + @Override + public RecyclerView.Adapter onCreateAdapter(@NonNull PreferenceScreen preferenceScreen) { + if (SdkLevel.isAtLeastV() && DeviceUtils.isHandheld(requireContext())) { + return new SectionPreferenceGroupAdapter(preferenceScreen); + } else { + return super.onCreateAdapter(preferenceScreen); + } + } + protected void setLoading(boolean loading, boolean animate) { setLoading(loading, animate, false); } 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 5a7c3f2b5..73366f4cd 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java @@ -77,8 +77,9 @@ import java.util.Random; * which permissions to grant to the app before first use and if an update changed the permissions. */ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat - implements View.OnClickListener, PermissionPreference.PermissionPreferenceChangeListener, - PermissionPreference.PermissionPreferenceOwnerFragment { + implements View.OnClickListener, + BasePermissionReviewPreference.PermissionPreferenceChangeListener, + BasePermissionReviewPreference.PermissionPreferenceOwnerFragment { private static final String EXTRA_PACKAGE_INFO = "com.android.permissioncontroller.permission.ui.extra.PACKAGE_INFO"; @@ -267,11 +268,11 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat PermissionControllerStatsLog.write(REVIEW_PERMISSIONS_FRAGMENT_RESULT_REPORTED, changeId, mViewModel.getPackageInfo().applicationInfo.uid, group.getPackageName(), - permission.getName(), permission.isGrantedIncludingAppOp()); + permission.getName(), permission.isGranted()); Log.i(LOG_TAG, "Permission grant via permission review changeId=" + changeId + " uid=" + mViewModel.getPackageInfo().applicationInfo.uid + " packageName=" + group.getPackageName() + " permission=" - + permission.getName() + " granted=" + permission.isGrantedIncludingAppOp()); + + permission.getName() + " granted=" + permission.isGranted()); } } @@ -369,7 +370,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat screen.addPreference(preference); } else { if (mNewPermissionsCategory == null) { - mNewPermissionsCategory = new PreferenceCategory(activity); + mNewPermissionsCategory = new PermissionPreferenceCategory(activity); mNewPermissionsCategory.setTitle(R.string.new_permissions_category); mNewPermissionsCategory.setOrder(1); screen.addPreference(mNewPermissionsCategory); @@ -378,7 +379,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat } } else { if (mCurrentPermissionsCategory == null) { - mCurrentPermissionsCategory = new PreferenceCategory(activity); + mCurrentPermissionsCategory = new PermissionPreferenceCategory(activity); mCurrentPermissionsCategory.setTitle(R.string.current_permissions_category); mCurrentPermissionsCategory.setOrder(2); screen.addPreference(mCurrentPermissionsCategory); @@ -447,7 +448,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat } /** - * Extend the {@link PermissionPreference}: + * Extend the {@link BasePermissionReviewPreference}: * <ul> * <li>Show the description of the permission group</li> * <li>Show the permission group as granted if the user has not toggled it yet. This means @@ -455,7 +456,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat * in {@link #confirmPermissionsReview()}.</li> * </ul> */ - private static class PermissionReviewPreference extends PermissionPreference { + private static class PermissionReviewPreference extends BasePermissionReviewPreference { private final LightAppPermGroup mGroup; private final Context mContext; private boolean mWasChanged; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java index 8745ac8f7..e16753e09 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java @@ -29,7 +29,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; @@ -180,7 +179,7 @@ public abstract class SettingsWithLargeHeader extends PermissionsFrameFragment /** * A Preference that will act as the "Large Header" for "SettingsWithLargeHeader" fragments. */ - public static class LargeHeaderPreference extends PreferenceCategory { + public static class LargeHeaderPreference extends PermissionPreferenceCategory { private SettingsWithLargeHeader mFragment; private LargeHeaderPreference(Context context, SettingsWithLargeHeader fragment) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt index b31b3e484..5f04b159e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt @@ -24,7 +24,6 @@ import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.preference.AndroidResources -import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.utils.KotlinUtils @@ -39,13 +38,12 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils * @param user The user whose package icon will be retrieved * @param context The current context */ -open class SmartIconLoadPackagePermissionPreference -constructor( +open class SmartIconLoadPackagePermissionPreference( private val app: Application, private val packageName: String, private val user: UserHandle, context: Context -) : Preference(context) { +) : PermissionPreference(context) { private var titleContentDescription: CharSequence? = null @@ -60,18 +58,14 @@ constructor( title.maxLines = 1 title.ellipsize = TextUtils.TruncateAt.END - val imageView = holder.findViewById(android.R.id.icon) as ImageView + val icon = holder.findViewById(android.R.id.icon) as ImageView + val iconSize = + context.resources.getDimensionPixelSize(R.dimen.permission_preference_app_icon_size) + icon.maxWidth = iconSize + icon.maxHeight = iconSize - imageView.maxWidth = - context.resources.getDimensionPixelSize( - com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size - ) - imageView.maxHeight = - context.resources.getDimensionPixelSize( - com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size - ) - imageView.setImageDrawable(KotlinUtils.getBadgedPackageIcon(app, packageName, user)) - imageView.visibility = View.VISIBLE + icon.setImageDrawable(KotlinUtils.getBadgedPackageIcon(app, packageName, user)) + icon.visibility = View.VISIBLE var imageFrame: View? = holder.findViewById(R.id.icon_frame) if (imageFrame == null) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppsFooterPreference.kt index 278243f09..9972682f5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppsFooterPreference.kt @@ -17,7 +17,6 @@ package com.android.permissioncontroller.permission.ui.handheld import android.content.Context -import android.util.AttributeSet import android.widget.TextView import androidx.preference.Preference import androidx.preference.PreferenceViewHolder @@ -27,15 +26,12 @@ import com.android.permissioncontroller.R * A Preference for the footer of a screen. Has two summaries, and adjusted spacing and icon * placement. */ -class FooterPreference : Preference { - constructor(c: Context) : super(c) - constructor(c: Context, a: AttributeSet) : super(c, a) - constructor(c: Context, a: AttributeSet, attr: Int) : super(c, a, attr) - constructor(c: Context, a: AttributeSet, attr: Int, res: Int) : super(c, a, attr, res) +class UnusedAppsFooterPreference(c: Context) : Preference(c) { init { - layoutResource = R.layout.footer_preference + layoutResource = R.layout.unused_apps_footer_preference } + var secondSummary: CharSequence = "" set(value) { secondSummaryView?.text = value diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java index f65f3d788..35650defb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.handheld; +// LINT.IfChange + +package com.android.permissioncontroller.permission.ui.handheld.max35; import static android.Manifest.permission_group.STORAGE; import static android.app.Activity.RESULT_OK; @@ -30,6 +32,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ 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.compat.AppPermissionFragmentCompat.PERSISTENT_DEVICE_ID; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; @@ -47,11 +50,13 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.text.BidiFormatter; +import android.util.ArrayMap; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; @@ -66,7 +71,7 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.core.widget.NestedScrollView; import androidx.fragment.app.DialogFragment; @@ -76,6 +81,10 @@ import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; +import com.android.permissioncontroller.permission.ui.handheld.AllAppPermissionsFragment; +import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment; +import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment; +import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; @@ -101,15 +110,12 @@ import java.util.Set; * * <p>Allows the user to control whether the app is granted the permission. */ -public class AppPermissionFragment extends SettingsWithLargeHeader +public class LegacyAppPermissionFragment extends SettingsWithLargeHeader implements AppPermissionViewModel.ConfirmDialogShowingFragment { - private static final String LOG_TAG = "AppPermissionFragment"; + private static final String LOG_TAG = "LegacyAppPermissionFragment"; private static final long POST_DELAY_MS = 20; private static final long EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS = 200L; - static final String GRANT_CATEGORY = "grant_category"; - static final String PERSISTENT_DEVICE_ID = "persistent_device_id"; - private @NonNull AppPermissionViewModel mViewModel; private @NonNull ViewGroup mAppPermissionRationaleContainer; private @NonNull ViewGroup mAppPermissionRationaleContent; @@ -119,10 +125,10 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private @NonNull RadioButton mAllowForegroundButton; private @NonNull RadioButton mAskOneTimeButton; private @NonNull RadioButton mAskButton; - private @NonNull RadioButton mAllowLimitedButton; + private @NonNull RadioButton mSelectButton; private @NonNull RadioButton mDenyButton; private @NonNull RadioButton mDenyForegroundButton; - private @NonNull ImageView mSelectPhotosButton; + private @NonNull ImageView mEditSelectedPhotosButton; private @NonNull View mAllowLimitedPhotosLayout; private @NonNull View mSelectPhotosDivider; private @NonNull View mLocationAccuracy; @@ -146,37 +152,6 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private Drawable mPackageIcon; private @NonNull RoleManager mRoleManager; - /** - * Create a bundle with the arguments needed by this fragment - * - * @param packageName The name of the package - * @param permName The name of the permission whose group this fragment is for (optional) - * @param groupName The name of the permission group (required if permName not specified) - * @param userHandle The user of the app permission group - * @param caller The name of the fragment we called from - * @param sessionId The current session ID - * @param grantCategory The grant status of this app permission group. Used to initially set - * the button state - * @return A bundle with all of the args placed - */ - public static Bundle createArgs(@NonNull String packageName, - @Nullable String permName, @Nullable String groupName, - @NonNull UserHandle userHandle, @Nullable String caller, long sessionId, @Nullable - String grantCategory) { - Bundle arguments = new Bundle(); - arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); - if (groupName == null) { - arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName); - } else { - arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName); - } - arguments.putParcelable(Intent.EXTRA_USER, userHandle); - arguments.putString(EXTRA_CALLER_NAME, caller); - arguments.putLong(EXTRA_SESSION_ID, sessionId); - arguments.putString(GRANT_CATEGORY, grantCategory); - return arguments; - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -280,7 +255,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); - mAllowLimitedButton = root.requireViewById(R.id.select_radio_button); + mSelectButton = root.requireViewById(R.id.select_radio_button); mDenyButton = root.requireViewById(R.id.deny_radio_button); mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button); @@ -290,7 +265,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mLocationAccuracy = root.requireViewById(R.id.location_accuracy); mLocationAccuracySwitch = root.requireViewById(R.id.location_accuracy_switch); mAllowLimitedPhotosLayout = root.requireViewById(R.id.radio_select_layout); - mSelectPhotosButton = root.requireViewById(R.id.edit_selected_button); + mEditSelectedPhotosButton = root.requireViewById(R.id.edit_selected_button); mSelectPhotosDivider = root.requireViewById(R.id.edit_photos_divider); mNestedScrollView = root.requireViewById(R.id.nested_scroll_view); @@ -307,7 +282,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mLocationAccuracy.setVisibility(View.GONE); mAllowLimitedPhotosLayout.setVisibility(View.GONE); mSelectPhotosDivider.setAlpha(0f); - mSelectPhotosButton.setAlpha(0f); + mEditSelectedPhotosButton.setAlpha(0f); } if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) { @@ -408,6 +383,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader } mAllowButtonFrame.setOnClickListener((v) -> allowButtonFrameClickListener()); mAllowAlwaysButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ALLOW_ALWAYS); if (mIsStorageGroup) { showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS, R.string.special_file_access_dialog, -1, false); @@ -418,6 +394,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader setResult(GRANTED_ALWAYS); }); mAllowForegroundButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ALLOW_FOREGROUND); if (mIsStorageGroup) { mViewModel.setAllFilesAccess(false); mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, @@ -429,19 +406,24 @@ public class AppPermissionFragment extends SettingsWithLargeHeader setResult(GRANTED_FOREGROUND_ONLY); } }); - // mAskOneTimeButton only shows if checked hence should do nothing + mAskOneTimeButton.setOnClickListener((v) -> { + // mAskOneTimeButton only shows if checked hence should do nothing + markSingleButtonAsChecked(ButtonType.ASK_ONCE); + }); mAskButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ASK); mViewModel.requestChange(true, this, this, ChangeRequest.REVOKE_BOTH, APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME); setResult(DENIED); }); - mAllowLimitedButton.setOnClickListener((v) -> { + mSelectButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.SELECT_PHOTOS); int buttonPressed = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED; mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED, buttonPressed); }); - mSelectPhotosButton.setOnClickListener((v) -> { + mEditSelectedPhotosButton.setOnClickListener((v) -> { ButtonState selectState = states.get(ButtonType.SELECT_PHOTOS); if (selectState != null && selectState.isChecked() && !mPhotoPickerTriggered) { mPhotoPickerTriggered = true; @@ -449,6 +431,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader } }); mDenyButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.DENY); if (mViewModel.getFullStorageStateLiveData().getValue() != null && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) { mViewModel.setAllFilesAccess(false); @@ -458,6 +441,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader setResult(DENIED_DO_NOT_ASK_AGAIN); }); mDenyForegroundButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.DENY_FOREGROUND); mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_FOREGROUND, APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND); setResult(DENIED_DO_NOT_ASK_AGAIN); @@ -485,8 +469,8 @@ 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(mAllowLimitedButton, states.get(ButtonType.SELECT_PHOTOS)); - if (mAllowLimitedButton.getVisibility() == View.VISIBLE) { + setButtonState(mSelectButton, states.get(ButtonType.SELECT_PHOTOS)); + if (mSelectButton.getVisibility() == View.VISIBLE) { mAllowButton.setText(R.string.app_permission_button_always_allow_all); } else { mAllowButton.setText(R.string.app_permission_button_allow); @@ -515,7 +499,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader if (!mAllowButton.isEnabled()) { mViewModel.handleDisabledAllowButton(this); } else { - mAllowButton.setChecked(true); + markSingleButtonAsChecked(ButtonType.ALLOW); mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND, APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); setResult(GRANTED_ALWAYS); @@ -533,16 +517,16 @@ public class AppPermissionFragment extends SettingsWithLargeHeader button.jumpDrawablesToCurrentState(); } - if (button == mAllowLimitedButton) { + if (button == mSelectButton) { mAllowLimitedPhotosLayout.setVisibility(visible); float endOpacity = state.isChecked() ? 1f : 0f; // On initial load, do not show the fade in/out animation if (mIsInitialLoad) { mSelectPhotosDivider.setAlpha(endOpacity); - mSelectPhotosButton.setAlpha(endOpacity); + mEditSelectedPhotosButton.setAlpha(endOpacity); return; } - mSelectPhotosButton.animate().alpha(endOpacity) + mEditSelectedPhotosButton.animate().alpha(endOpacity) .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); mSelectPhotosDivider.animate().alpha(endOpacity) .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); @@ -670,6 +654,24 @@ public class AppPermissionFragment extends SettingsWithLargeHeader ConfirmDialog.class.getName()); } + private void markSingleButtonAsChecked(ButtonType buttonToMarkChecked) { + if (!mViewModel.getButtonStateLiveData().isInitialized()) { + return; + } + Map<ButtonType, ButtonState> currentStates = mViewModel.getButtonStateLiveData().getValue(); + Map<ButtonType, ButtonState> newStates = new ArrayMap<>(); + for (ButtonType button: currentStates.keySet()) { + ButtonState state = currentStates.get(button); + boolean isChecked = button == buttonToMarkChecked + // Don't uncheck the Location Accuracy switch, if it is checked + || (button == ButtonType.LOCATION_ACCURACY && state.isChecked()); + ButtonState newState = new ButtonState(isChecked, state.isEnabled(), state.isShown(), + state.getCustomRequest()); + newStates.put(button, newState); + } + setRadioButtonsState(newStates); + } + /** * A dialog warning the user that they are about to deny a permission that was granted by * default, or that they are denying a permission on a Pre-M app @@ -690,7 +692,8 @@ public class AppPermissionFragment extends SettingsWithLargeHeader public Dialog onCreateDialog(Bundle savedInstanceState) { // TODO(b/229024576): This code is duplicated, refactor ConfirmDialog for easier // NFF sharing - AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); + LegacyAppPermissionFragment fragment = + (LegacyAppPermissionFragment) getParentFragment(); boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) == ChangeRequest.GRANT_ALL_FILE_ACCESS; int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; @@ -721,12 +724,14 @@ public class AppPermissionFragment extends SettingsWithLargeHeader @Override public void onCancel(DialogInterface dialog) { - AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); + LegacyAppPermissionFragment fragment = + (LegacyAppPermissionFragment) getParentFragment(); fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue()); } } @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void showAdvancedConfirmDialog(AdvancedConfirmDialogArgs args) { AlertDialog.Builder b = new AlertDialog.Builder(getContext()) .setIcon(args.getIconId()) @@ -741,7 +746,8 @@ public class AppPermissionFragment extends SettingsWithLargeHeader .setPositiveButton(args.getPositiveButtonTextId(), (DialogInterface dialog, int which) -> { mViewModel.requestChange(args.getSetOneTime(), - AppPermissionFragment.this, AppPermissionFragment.this, + LegacyAppPermissionFragment.this, + LegacyAppPermissionFragment.this, args.getChangeRequest(), args.getButtonClicked()); }); if (args.getTitleId() != 0) { @@ -750,3 +756,4 @@ public class AppPermissionFragment extends SettingsWithLargeHeader b.show(); } } +// LINT.ThenChange(../v36/AppPermissionFragment.java) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java index 5c20ef9df..933911bff 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java @@ -18,8 +18,10 @@ package com.android.permissioncontroller.permission.ui.handheld.v31; import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION; import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__INFO_ICON_CLICKED; +import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__TIMELINE_ROW_CLICKED; import static com.android.permissioncontroller.PermissionControllerStatsLog.write; +import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -28,6 +30,7 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.text.format.DateFormat; +import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -47,8 +50,8 @@ import com.android.permissioncontroller.permission.compat.IntentCompat; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel; import com.android.permissioncontroller.permission.utils.Utils; -import java.util.ArrayList; import java.util.Objects; +import java.util.Set; /** * Preference for the permission history page @@ -64,26 +67,22 @@ public class PermissionHistoryPreference extends Preference { private final long mAccessStartTime; private final long mAccessEndTime; private final Drawable mAppIcon; - private final String mTitle; - private final ArrayList<String> mAttributionTags; + private final Set<String> mAttributionTags; private final boolean mIsLastUsage; - private final Intent mIntent; + private Intent mIntent = null; + private boolean mIntentLoaded = false; private final boolean mShowingAttribution; private final PackageManager mUserPackageManager; + private final boolean mIsEmergencyLocationAccess; private final long mSessionId; - private Drawable mWidgetIcon; - - public PermissionHistoryPreference(@NonNull Context context, - @NonNull UserHandle userHandle, @NonNull String pkgName, - @Nullable Drawable appIcon, - @NonNull String preferenceTitle, - @NonNull String permissionGroup, - @NonNull long accessStartTime, - @NonNull long accessEndTime, - @Nullable CharSequence summaryText, boolean showingAttribution, - @NonNull ArrayList<String> attributionTags, boolean isLastUsage, long sessionId) { + public PermissionHistoryPreference(@NonNull Context context, @NonNull UserHandle userHandle, + @NonNull String pkgName, @Nullable Drawable packageIcon, @NonNull String packageLabel, + @NonNull String permissionGroup, @NonNull long accessStartTime, + @NonNull long accessEndTime, @Nullable CharSequence summaryText, + boolean showingAttribution, @NonNull Set<String> attributionTags, + boolean isLastUsage, long sessionId, boolean isEmergencyLocationAccess) { super(context); mContext = context; Context userContext = Utils.getUserContext(context, userHandle); @@ -93,24 +92,18 @@ public class PermissionHistoryPreference extends Preference { mPermissionGroup = permissionGroup; mAccessStartTime = accessStartTime; mAccessEndTime = accessEndTime; - mAppIcon = appIcon; - mTitle = preferenceTitle; - mWidgetIcon = null; + mAppIcon = packageIcon; mAttributionTags = attributionTags; mIsLastUsage = isLastUsage; mSessionId = sessionId; mShowingAttribution = showingAttribution; + mIsEmergencyLocationAccess = isEmergencyLocationAccess; - setTitle(mTitle); + setTitle(packageLabel); if (summaryText != null) { setSummary(summaryText); } - - mIntent = getViewPermissionUsageForPeriodIntent(showingAttribution); - if (mIntent != null) { - mWidgetIcon = mContext.getDrawable(R.drawable.ic_info_outline); - setWidgetLayoutResource(R.layout.image_view_with_divider); - } + setWidgetLayoutResource(R.layout.image_view_with_divider); } @Override @@ -135,13 +128,14 @@ public class PermissionHistoryPreference extends Preference { widgetFrameParent.setGravity(Gravity.TOP); TextView permissionHistoryTime = widget.findViewById(R.id.permission_history_time); - permissionHistoryTime.setText(DateFormat.getTimeFormat(mContext).format(mAccessEndTime)); + permissionHistoryTime.setText(DateFormat.getTimeFormat(mContext).format(mAccessStartTime)); - ImageView permissionIcon = widget.findViewById(R.id.permission_history_icon); - permissionIcon.setImageDrawable(mAppIcon); + ImageView appIcon = widget.findViewById(R.id.permission_history_icon); + appIcon.setImageDrawable(mAppIcon); ImageView widgetView = widgetFrame.findViewById(R.id.icon); - setInfoIcon(holder, widgetView); + View dividerVerticalBar = widgetFrame.findViewById(R.id.divider); + setInfoIcon(holder, widgetView, dividerVerticalBar); View dashLine = widget.findViewById(R.id.permission_history_dash_line); dashLine.setVisibility(mIsLastUsage ? View.GONE : View.VISIBLE); @@ -150,24 +144,58 @@ public class PermissionHistoryPreference extends Preference { // It's temporarily created via a static method due to ongoing ViewModel refactoring. Intent intent = PermissionUsageDetailsViewModel.Companion.createHistoryPreferenceClickIntent( - mContext, - mUserHandle, - mPackageName, - mPermissionGroup, - mAccessStartTime, - mAccessEndTime, - mShowingAttribution, - mAttributionTags); - - setOnPreferenceClickListener((preference) -> { - mContext.startActivityAsUser(intent, mUserHandle); - return true; - }); + mContext, + mUserHandle, + mPackageName, + mPermissionGroup, + mAccessStartTime, + mAccessEndTime, + mShowingAttribution, + mAttributionTags); + + if (mIsEmergencyLocationAccess) { + setOnPreferenceClickListener(preference -> { + write(PERMISSION_DETAILS_INTERACTION, + mSessionId, + mPermissionGroup, + mPackageName, + PERMISSION_DETAILS_INTERACTION__ACTION__TIMELINE_ROW_CLICKED); + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext) + .setTitle(R.string.privacy_dashboard_emergency_location_dialog_title) + .setMessage( + R.string.privacy_dashboard_emergency_location_dialog_description) + .setPositiveButton(R.string.app_permissions, (dialog, which) -> { + mContext.startActivityAsUser(intent, mUserHandle); + }) + .setNegativeButton(R.string.dialog_close, null); + + AlertDialog alertDialog = dialogBuilder.create(); + alertDialog.show(); + TextView messageView = alertDialog.findViewById(android.R.id.message); + if (messageView != null) { + messageView.setMovementMethod(LinkMovementMethod.getInstance()); + } + return true; + }); + } else { + setOnPreferenceClickListener((preference) -> { + write(PERMISSION_DETAILS_INTERACTION, + mSessionId, + mPermissionGroup, + mPackageName, + PERMISSION_DETAILS_INTERACTION__ACTION__TIMELINE_ROW_CLICKED); + mContext.startActivityAsUser(intent, mUserHandle); + return true; + }); + } } - private void setInfoIcon(@NonNull PreferenceViewHolder holder, ImageView widgetView) { - if (mIntent != null) { - widgetView.setImageDrawable(mWidgetIcon); + private void setInfoIcon(@NonNull PreferenceViewHolder holder, ImageView widgetView, + View dividerVerticalBar) { + Intent intent = getViewPermissionUsageForPeriodIntent(); + if (intent != null) { + dividerVerticalBar.setVisibility(View.VISIBLE); + widgetView.setImageDrawable(mContext.getDrawable(R.drawable.ic_info_outline)); widgetView.setOnClickListener(v -> { write(PERMISSION_DETAILS_INTERACTION, mSessionId, @@ -175,7 +203,7 @@ public class PermissionHistoryPreference extends Preference { mPackageName, PERMISSION_DETAILS_INTERACTION__ACTION__INFO_ICON_CLICKED); try { - mContext.startActivityAsUser(mIntent, mUserHandle); + mContext.startActivityAsUser(intent, mUserHandle); } catch (ActivityNotFoundException e) { Log.e(LOG_TAG, "No activity found for viewing permission usage."); } @@ -183,6 +211,9 @@ public class PermissionHistoryPreference extends Preference { View preferenceRootView = holder.itemView; preferenceRootView.setPaddingRelative(preferenceRootView.getPaddingStart(), preferenceRootView.getPaddingTop(), 0, preferenceRootView.getPaddingBottom()); + } else { + dividerVerticalBar.setVisibility(View.GONE); + widgetView.setImageDrawable(null); } } @@ -191,7 +222,10 @@ public class PermissionHistoryPreference extends Preference { * can't be handled. */ @Nullable - private Intent getViewPermissionUsageForPeriodIntent(boolean showingAttribution) { + private Intent getViewPermissionUsageForPeriodIntent() { + if (mIntentLoaded) { + return mIntent; + } Intent viewUsageIntent = new Intent(); viewUsageIntent.setAction(Intent.ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD); viewUsageIntent.setPackage(mPackageName); @@ -201,7 +235,7 @@ public class PermissionHistoryPreference extends Preference { viewUsageIntent.putExtra(Intent.EXTRA_START_TIME, mAccessStartTime); viewUsageIntent.putExtra(Intent.EXTRA_END_TIME, mAccessEndTime); - viewUsageIntent.putExtra(IntentCompat.EXTRA_SHOWING_ATTRIBUTION, showingAttribution); + viewUsageIntent.putExtra(IntentCompat.EXTRA_SHOWING_ATTRIBUTION, mShowingAttribution); viewUsageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ResolveInfo resolveInfo = mUserPackageManager.resolveActivity(viewUsageIntent, @@ -209,8 +243,9 @@ public class PermissionHistoryPreference extends Preference { if (resolveInfo != null && resolveInfo.activityInfo != null && Objects.equals( resolveInfo.activityInfo.permission, android.Manifest.permission.START_VIEW_PERMISSION_USAGE)) { - return viewUsageIntent; + mIntent = viewUsageIntent; } - return null; + mIntentLoaded = true; + return mIntent; } }
\ No newline at end of file diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java index 621657016..3a904c466 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java @@ -18,6 +18,11 @@ 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.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION; +import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__MANAGE_PERMISSIONS_CLICKED; +import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__SHOW_7DAYS_CLICKED; +import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED; +import static com.android.permissioncontroller.PermissionControllerStatsLog.write; import android.app.ActionBar; import android.app.Activity; @@ -37,7 +42,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.lifecycle.ViewModelProvider; @@ -50,9 +54,9 @@ import com.android.permissioncontroller.PermissionControllerApplication; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader; -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel; +import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo; -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiInfo; +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState; import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory; import com.android.permissioncontroller.permission.utils.KotlinUtils; @@ -76,16 +80,16 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { 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 @Nullable String mPermissionGroup; - private int mUsageSubtitle; + private String mPermissionGroup; private boolean mHasSystemApps; + private boolean mMenuItemsCreated = false; private MenuItem mShowSystemMenu; private MenuItem mHideSystemMenu; private MenuItem mShow7DaysDataMenu; private MenuItem mShow24HoursDataMenu; - private PermissionUsageDetailsViewModel mViewModel; + private BasePermissionUsageDetailsViewModel mViewModel; private long mSessionId; @@ -93,17 +97,15 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPermissionGroup = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME); - if (mPermissionGroup == null) { Log.e(TAG, "No permission group was provided for PermissionDetailsFragment"); return; } - PermissionUsageDetailsViewModelFactory factory = new PermissionUsageDetailsViewModelFactory( PermissionControllerApplication.get(), this, mPermissionGroup); mViewModel = - new ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel.class); + new ViewModelProvider(this, factory).get(BasePermissionUsageDetailsViewModel.class); if (savedInstanceState != null) { mSessionId = savedInstanceState.getLong(SESSION_ID_KEY); @@ -114,9 +116,7 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { mViewModel.updateShowSystemAppsToggle( getArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false)); mViewModel.updateShow7DaysToggle( - KotlinUtils.INSTANCE.is7DayToggleEnabled() - && getArguments() - .getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false)); + getArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false)); setHasOptionsMenu(true); ActionBar ab = getActivity().getActionBar(); @@ -163,6 +163,11 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { extendedFab.setVisibility(View.VISIBLE); extendedFab.setOnClickListener( view -> { + write(PERMISSION_DETAILS_INTERACTION, + mSessionId, + mPermissionGroup, + null, + PERMISSION_DETAILS_INTERACTION__ACTION__MANAGE_PERMISSIONS_CLICKED); Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) .putExtra(Intent.EXTRA_PERMISSION_NAME, mPermissionGroup); @@ -190,9 +195,9 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { .getString( R.string.permission_group_usage_title, KotlinUtils.INSTANCE.getPermGroupLabel( - getActivity(), mPermissionGroup)); + requireActivity(), mPermissionGroup)); } - getActivity().setTitle(title); + requireActivity().setTitle(title); } @Override @@ -207,31 +212,21 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { 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); - boolean showSystem = false; - if (mViewModel.getShowSystemLiveData().getValue() != null) { - showSystem = mViewModel.getShowSystemLiveData().getValue(); - } - updateShowSystemToggle(showSystem); - - 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); - boolean show7Days = false; - if (mViewModel.getShow7DaysLiveData().getValue() != null) { - show7Days = mViewModel.getShow7DaysLiveData().getValue(); - } - updateShow7DaysToggle(show7Days); - } + 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); + mMenuItemsCreated = true; + updateShow7DaysToggle(mViewModel.getShow7Days()); + updateShowSystemToggle(mViewModel.getShowSystem()); } @Override @@ -239,16 +234,26 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { int itemId = item.getItemId(); switch (itemId) { case android.R.id.home: - getActivity().finishAfterTransition(); + requireActivity().finishAfterTransition(); return true; case MENU_SHOW_SYSTEM: + write(PERMISSION_DETAILS_INTERACTION, + mSessionId, + mPermissionGroup, + null, + PERMISSION_DETAILS_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED); mViewModel.updateShowSystemAppsToggle(true); break; case MENU_HIDE_SYSTEM: mViewModel.updateShowSystemAppsToggle(false); break; case MENU_SHOW_7_DAYS_DATA: - mViewModel.updateShow7DaysToggle(KotlinUtils.INSTANCE.is7DayToggleEnabled()); + write(PERMISSION_DETAILS_INTERACTION, + mSessionId, + mPermissionGroup, + null, + PERMISSION_DETAILS_INTERACTION__ACTION__SHOW_7DAYS_CLICKED); + mViewModel.updateShow7DaysToggle(true); break; case MENU_SHOW_24_HOURS_DATA: mViewModel.updateShow7DaysToggle(false); @@ -259,10 +264,12 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { } /** Updates page content and menu items. */ - private void updateAllUI(PermissionUsageDetailsUiInfo uiData) { - if (getActivity() == null) { + private void updateAllUI(PermissionUsageDetailsUiState uiInfo) { + if (getActivity() == null || uiInfo instanceof PermissionUsageDetailsUiState.Loading) { return; } + PermissionUsageDetailsUiState.Success uiData = + (PermissionUsageDetailsUiState.Success) uiInfo; Context context = getActivity(); PreferenceScreen screen = getPreferenceScreen(); if (screen == null) { @@ -270,50 +277,34 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { setPreferenceScreen(screen); } screen.removeAll(); - boolean show7Days = - mViewModel.getShow7DaysLiveData().getValue() != null - ? mViewModel.getShow7DaysLiveData().getValue() - : false; - Preference subtitlePreference = new Preference(context); - updateShow7DaysToggle(show7Days); - mUsageSubtitle = - show7Days - ? R.string.permission_group_usage_subtitle_7d - : R.string.permission_group_usage_subtitle_24h; + updateShow7DaysToggle(uiData.getShow7Days()); + int usageSubtitle = uiData.getShow7Days() + ? R.string.permission_group_usage_subtitle_7d + : R.string.permission_group_usage_subtitle_24h; subtitlePreference.setSummary( getResources() .getString( - mUsageSubtitle, + usageSubtitle, KotlinUtils.INSTANCE.getPermGroupLabel( - getActivity(), mPermissionGroup))); + context, mPermissionGroup))); subtitlePreference.setSelectable(false); screen.addPreference(subtitlePreference); - boolean containsSystemAppAccesses = uiData.getContainsSystemAppAccesses(); + boolean containsSystemAppAccesses = uiData.getContainsSystemAppUsage(); if (mHasSystemApps != containsSystemAppAccesses) { mHasSystemApps = containsSystemAppAccesses; } - boolean showSystem = - mViewModel.getShowSystemLiveData().getValue() != null - ? mViewModel.getShowSystemLiveData().getValue() - : false; - updateShowSystemToggle(showSystem); + updateShowSystemToggle(uiData.getShowSystem()); // Make these variables effectively final so that // we can use these captured variables in the below lambda expression AtomicReference<PreferenceCategory> category = new AtomicReference<>(createDayCategoryPreference()); screen.addPreference(category.get()); - PreferenceScreen finalScreen = screen; - - if (getActivity() == null) { - // Fragment has no Activity, return. - return; - } - renderHistoryPreferences(uiData.getAppPermissionAccessUiInfoList(), category, finalScreen); + renderHistoryPreferences(uiData.getAppPermissionAccessUiInfoList(), category, screen); setLoading(false, true); setProgressBarVisible(false); } @@ -323,8 +314,7 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { List<AppPermissionAccessUiInfo> appPermissionAccessUiInfoList, AtomicReference<PreferenceCategory> category, PreferenceScreen preferenceScreen) { - Context context = getContext(); - long previousDateMs = 0L; + Context context = requireContext(); long midnightToday = ZonedDateTime.now(ZoneId.systemDefault()) .truncatedTo(ChronoUnit.DAYS) @@ -337,19 +327,20 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { .toEpochSecond() * 1000L; + long previousAccessDateMs = 0L; + ZoneId zoneId = Clock.system(ZoneId.systemDefault()).getZone(); + for (int i = 0; i < appPermissionAccessUiInfoList.size(); i++) { AppPermissionAccessUiInfo appPermissionAccessUiInfo = appPermissionAccessUiInfoList.get(i); long accessEndTime = appPermissionAccessUiInfo.getAccessEndTime(); - long currentDateMs = - ZonedDateTime.ofInstant( - Instant.ofEpochMilli(accessEndTime), - Clock.system(ZoneId.systemDefault()).getZone()) - .truncatedTo(ChronoUnit.DAYS) - .toEpochSecond() + long accessDateMS = + ZonedDateTime.ofInstant(Instant.ofEpochMilli(accessEndTime), zoneId) + .truncatedTo(ChronoUnit.DAYS) + .toEpochSecond() * 1000L; - if (currentDateMs != previousDateMs) { - if (previousDateMs != 0L) { + if (accessDateMS != previousAccessDateMs) { + if (previousAccessDateMs != 0L) { category.set(createDayCategoryPreference()); preferenceScreen.addPreference(category.get()); } @@ -359,14 +350,14 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { category.get().setTitle(R.string.permission_history_category_yesterday); } else { category.get() - .setTitle(DateFormat.getLongDateFormat(context).format(currentDateMs)); + .setTitle(DateFormat.getLongDateFormat(context).format(accessDateMS)); } - previousDateMs = currentDateMs; + previousAccessDateMs = accessDateMS; } Preference permissionUsagePreference = new PermissionHistoryPreference( - getContext(), + context, appPermissionAccessUiInfo.getUserHandle(), appPermissionAccessUiInfo.getPackageName(), appPermissionAccessUiInfo.getBadgedPackageIcon(), @@ -378,44 +369,36 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { appPermissionAccessUiInfo.getShowingAttribution(), appPermissionAccessUiInfo.getAttributionTags(), i == appPermissionAccessUiInfoList.size() - 1, - mSessionId); + mSessionId, + appPermissionAccessUiInfo.isEmergencyLocationAccess()); category.get().addPreference(permissionUsagePreference); } } private void updateShowSystemToggle(boolean showSystem) { + if (!mMenuItemsCreated) return; + if (mHasSystemApps) { - if (mShowSystemMenu != null) { - mShowSystemMenu.setVisible(!showSystem); - mShowSystemMenu.setEnabled(true); - } + mShowSystemMenu.setVisible(!showSystem); + mShowSystemMenu.setEnabled(true); - if (mHideSystemMenu != null) { - mHideSystemMenu.setVisible(showSystem); - mHideSystemMenu.setEnabled(true); - } + mHideSystemMenu.setVisible(showSystem); + mHideSystemMenu.setEnabled(true); } else { - if (mShowSystemMenu != null) { - mShowSystemMenu.setVisible(true); - mShowSystemMenu.setEnabled(false); - } + mShowSystemMenu.setVisible(true); + mShowSystemMenu.setEnabled(false); - if (mHideSystemMenu != null) { - mHideSystemMenu.setVisible(false); - mHideSystemMenu.setEnabled(false); - } + mHideSystemMenu.setVisible(false); + mHideSystemMenu.setEnabled(false); } } private void updateShow7DaysToggle(boolean show7Days) { - if (mShow7DaysDataMenu != null) { - mShow7DaysDataMenu.setVisible(!show7Days); - } + if (!mMenuItemsCreated) return; - if (mShow24HoursDataMenu != null) { - mShow24HoursDataMenu.setVisible(show7Days); - } + mShow7DaysDataMenu.setVisible(!show7Days); + mShow24HoursDataMenu.setVisible(show7Days); } private PreferenceCategory createDayCategoryPreference() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java index 7102fae9a..3fa780145 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java @@ -20,6 +20,7 @@ import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION; 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_7DAYS_CLICKED; import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED; import static com.android.permissioncontroller.PermissionControllerStatsLog.write; @@ -46,8 +47,6 @@ import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLarge import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel; import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModelFactory; import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState; -import com.android.permissioncontroller.permission.utils.KotlinUtils; -import com.android.settingslib.HelpUtils; import java.util.ArrayList; import java.util.Comparator; @@ -89,6 +88,7 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { private MenuItem mShow7DaysDataMenu; private MenuItem mShow24HoursDataMenu; private boolean mOtherExpanded; + private boolean mMenuItemsCreated = false; private PermissionUsageGraphicPreference mGraphic; @@ -105,7 +105,7 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { } PermissionUsageViewModelFactory factory = new PermissionUsageViewModelFactory( - getActivity().getApplication(), this, new Bundle()); + getActivity().getApplication()); mViewModel = new ViewModelProvider(this, factory) .get(PermissionUsageViewModel.class); @@ -198,26 +198,21 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { 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); + 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); + mMenuItemsCreated = true; + updateShow7DaysToggle(mViewModel.getShow7DaysData()); updateShowSystemToggle(mViewModel.getShowSystemApps()); - - 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); - updateShow7DaysToggle(mViewModel.getShow7DaysData()); - } - - HelpUtils.prepareHelpMenuItem( - getActivity(), menu, R.string.help_permission_usage, getClass().getName()); } @Override @@ -232,16 +227,20 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { PERMISSION_USAGE_FRAGMENT_INTERACTION, mSessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED); - updateAllUI(mViewModel.updateShowSystem(true)); + mViewModel.updateShowSystem(true); break; case MENU_HIDE_SYSTEM: - updateAllUI(mViewModel.updateShowSystem(false)); + mViewModel.updateShowSystem(false); break; case MENU_SHOW_7_DAYS_DATA: - updateAllUI(mViewModel.updateShow7Days(KotlinUtils.INSTANCE.is7DayToggleEnabled())); + write( + PERMISSION_USAGE_FRAGMENT_INTERACTION, + mSessionId, + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_7DAYS_CLICKED); + mViewModel.updateShow7Days(true); break; case MENU_SHOW_24_HOURS_DATA: - updateAllUI(mViewModel.updateShow7Days(false)); + mViewModel.updateShow7Days(false); break; } return super.onOptionsItemSelected(item); @@ -261,31 +260,28 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { } private void updateShowSystemToggle(boolean showSystem) { + if (!mMenuItemsCreated) return; + if (mHasSystemApps) { - if (mShowSystemMenu != null) { - mShowSystemMenu.setVisible(!showSystem); - } - if (mHideSystemMenu != null) { - mHideSystemMenu.setVisible(showSystem); - } + mShowSystemMenu.setVisible(!showSystem); + mShowSystemMenu.setEnabled(true); + + mHideSystemMenu.setVisible(showSystem); + mHideSystemMenu.setEnabled(true); } else { - if (mShowSystemMenu != null) { - mShowSystemMenu.setVisible(false); - } - if (mHideSystemMenu != null) { - mHideSystemMenu.setVisible(false); - } + mShowSystemMenu.setVisible(true); + mShowSystemMenu.setEnabled(false); + + mHideSystemMenu.setVisible(false); + mHideSystemMenu.setEnabled(false); } } private void updateShow7DaysToggle(boolean show7Days) { - if (mShow7DaysDataMenu != null) { - mShow7DaysDataMenu.setVisible(!show7Days); - } + if (!mMenuItemsCreated) return; - if (mShow24HoursDataMenu != null) { - mShow24HoursDataMenu.setVisible(show7Days); - } + mShow7DaysDataMenu.setVisible(!show7Days); + mShow24HoursDataMenu.setVisible(show7Days); } /** Updates page content and menu items. */ @@ -318,7 +314,6 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { mSessionId, PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED); }); - boolean containsSystemAppUsages = permissionUsagesUiData.getShouldShowSystemToggle(); Map<String, Integer> permissionGroupWithUsageCounts = permissionUsagesUiData.getPermissionGroupUsageCount(); List<Map.Entry<String, Integer>> permissionGroupWithUsageCountsEntries = @@ -332,13 +327,14 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { mViewModel.getPermissionGroupLabel( context, permissionGroupWithUsageCount.getKey()))); + boolean containsSystemAppUsages = permissionUsagesUiData.getContainsSystemAppUsage(); if (mHasSystemApps != containsSystemAppUsages) { mHasSystemApps = containsSystemAppUsages; } - - boolean show7Days = mViewModel.getShow7DaysData(); + boolean show7Days = permissionUsagesUiData.getShow7Days(); + boolean showSystem = permissionUsagesUiData.getShowSystem(); updateShow7DaysToggle(show7Days); - updateShowSystemToggle(mViewModel.getShowSystemApps()); + updateShowSystemToggle(showSystem); mGraphic = new PermissionUsageGraphicPreference(context, show7Days); screen.addPreference(mGraphic); @@ -351,7 +347,8 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { getAdvancedInfoSummaryString(context, permissionGroupWithUsageCountsEntries); screen.setSummary(advancedInfoSummary); - addUIContent(context, permissionGroupWithUsageCountsEntries, category); + addUIContent(context, permissionGroupWithUsageCountsEntries, category, + showSystem, show7Days); } private CharSequence getAdvancedInfoSummaryString( @@ -404,10 +401,10 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader { private void addUIContent( Context context, List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts, - PreferenceCategory category) { - boolean showSystem = mViewModel.getShowSystemApps(); - boolean show7Days = mViewModel.getShow7DaysData(); - + PreferenceCategory category, + boolean showSystem, + boolean show7Days + ) { for (int i = 0; i < permissionGroupWithUsageCounts.size(); i++) { Map.Entry<String, Integer> permissionGroupWithUsageCount = permissionGroupWithUsageCounts.get(i); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/DrawableStateLinearLayout.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/DrawableStateLinearLayout.kt new file mode 100644 index 000000000..e24ba1fab --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/DrawableStateLinearLayout.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld.v35 + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes + +/** This is a simple wrapper for [LinearLayout] that allows setting an extra drawable state. */ +class DrawableStateLinearLayout : LinearLayout { + var extraDrawableState: IntArray? = null + set(value) { + if (field != value) { + field = value + refreshDrawableState() + } + } + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + @StyleRes defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun onCreateDrawableState(extraSpace: Int): IntArray { + val extraDrawableState = + extraDrawableState ?: return super.onCreateDrawableState(extraSpace) + return mergeDrawableStates( + super.onCreateDrawableState(extraSpace + extraDrawableState.size), + extraDrawableState + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt new file mode 100644 index 000000000..e5dce40b0 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld.v35 + +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceGroupAdapter +import androidx.preference.PreferenceViewHolder +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference +import com.android.settingslib.widget.FooterPreference + +/** + * This is an extension over [PreferenceGroupAdapter] that allows creating visual sections for + * preferences. It sets the following drawable states on item views when they are + * [DrawableStateLinearLayout]: + * - [android.R.attr.state_single] if the item is the only one in a section + * - [android.R.attr.state_first] if the item is the first one in a section + * - [android.R.attr.state_middle] if the item is neither the first one or the last one in a section + * - [android.R.attr.state_last] if the item is the last one in a section + * - [R.attr.state_has_icon_space] if the item has icon space + * + * Note that [androidx.preference.PreferenceManager.PreferenceComparisonCallback] isn't supported + * (yet). + */ +class SectionPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) : + PreferenceGroupAdapter(preferenceGroup) { + private var itemPositionStates = intArrayOf() + + private val handler = Handler(Looper.getMainLooper()) + + private val syncRunnable = Runnable { buildItemPositionStates() } + + init { + buildItemPositionStates() + } + + override fun onPreferenceHierarchyChange(preference: Preference) { + super.onPreferenceHierarchyChange(preference) + + // Post after super class has posted their sync runnable to update preferences. + handler.removeCallbacks(syncRunnable) + handler.post(syncRunnable) + } + + private fun buildItemPositionStates() { + val itemCount = itemCount + if (itemPositionStates.size != itemCount) { + itemPositionStates = IntArray(itemCount) + } + + var lastItemIndex = -1 + for (i in 0..<itemCount) { + val preference = getItem(i)!! + + if (preference.isSectionDivider) { + itemPositionStates[i] = 0 + continue + } + + val isFirstItemInFirstSection = lastItemIndex == -1 + val isFirstItemInNewSection = lastItemIndex != i - 1 + itemPositionStates[i] = + if (isFirstItemInFirstSection || isFirstItemInNewSection) { + android.R.attr.state_first + } else { + android.R.attr.state_middle + } + if (!isFirstItemInFirstSection && isFirstItemInNewSection) { + itemPositionStates[lastItemIndex] = + if (itemPositionStates[lastItemIndex] == android.R.attr.state_first) { + android.R.attr.state_single + } else { + android.R.attr.state_last + } + } + + lastItemIndex = i + } + + if (lastItemIndex != -1) { + itemPositionStates[lastItemIndex] = + if (itemPositionStates[lastItemIndex] == android.R.attr.state_first) { + android.R.attr.state_single + } else { + android.R.attr.state_last + } + } + } + + private val Preference.isSectionDivider: Boolean + get() = + this is PreferenceCategory || + this is FooterPreference || + this is AppPermissionFooterLinkPreference + + override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + + val drawableStateLinearLayout = holder.itemView as? DrawableStateLinearLayout ?: return + val positionState = itemPositionStates.getOrElse(position) { 0 } + if (positionState == 0) { + return + } + val iconFrame = + holder.findViewById(androidx.preference.R.id.icon_frame) + ?: holder.findViewById(android.R.id.icon_frame) + val hasIconSpace = iconFrame != null && iconFrame.visibility != View.GONE + drawableStateLinearLayout.extraDrawableState = + when (positionState) { + android.R.attr.state_single -> + if (hasIconSpace) STATE_SET_SINGLE_HAS_ICON_SPACE else STATE_SET_SINGLE + android.R.attr.state_first -> + if (hasIconSpace) STATE_SET_FIRST_HAS_ICON_SPACE else STATE_SET_FIRST + android.R.attr.state_middle -> + if (hasIconSpace) STATE_SET_MIDDLE_HAS_ICON_SPACE else STATE_SET_MIDDLE + android.R.attr.state_last -> + if (hasIconSpace) STATE_SET_LAST_HAS_ICON_SPACE else STATE_SET_LAST + else -> error(positionState) + } + } + + companion object { + private val STATE_SET_SINGLE = intArrayOf(android.R.attr.state_single) + private val STATE_SET_FIRST = intArrayOf(android.R.attr.state_first) + private val STATE_SET_MIDDLE = intArrayOf(android.R.attr.state_middle) + private val STATE_SET_LAST = intArrayOf(android.R.attr.state_last) + private val STATE_SET_SINGLE_HAS_ICON_SPACE = + intArrayOf(android.R.attr.state_single, R.attr.state_has_icon_space) + private val STATE_SET_FIRST_HAS_ICON_SPACE = + intArrayOf(android.R.attr.state_first, R.attr.state_has_icon_space) + private val STATE_SET_MIDDLE_HAS_ICON_SPACE = + intArrayOf(android.R.attr.state_middle, R.attr.state_has_icon_space) + private val STATE_SET_LAST_HAS_ICON_SPACE = + intArrayOf(android.R.attr.state_last, R.attr.state_has_icon_space) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt new file mode 100644 index 000000000..01554880a --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld.v36 + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.widget.TextView +import androidx.annotation.AttrRes +import androidx.annotation.RequiresApi +import androidx.annotation.StyleRes +import androidx.preference.PreferenceViewHolder +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.handheld.PermissionPreference + +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +class AppPermissionFooterLinkPreference : PermissionPreference { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + @StyleRes defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) + + init { + layoutResource = R.layout.app_permission_footer_link_preference + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + if ( + context.resources.getBoolean( + R.bool.config_appPermissionFooterLinkPreferenceSummaryUnderlined + ) + ) { + val summary = holder.findViewById(android.R.id.summary) as TextView + summary.paint.isUnderlineText = true + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java new file mode 100644 index 000000000..4fde26c9d --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// LINT.IfChange + +package com.android.permissioncontroller.permission.ui.handheld.v36; + +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; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND; +import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME; +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.compat.AppPermissionFragmentCompat.PERSISTENT_DEVICE_ID; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; +import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; +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.ManagePermissionsActivity.EXTRA_CALLER_NAME; +import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; +import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; +import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; + +import android.app.ActionBar; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.role.RoleManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.text.BidiFormatter; +import android.util.ArrayMap; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.annotation.StringRes; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.preference.Preference; +import androidx.preference.TwoStatePreference; + +import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; +import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; +import com.android.permissioncontroller.permission.ui.handheld.AllAppPermissionsFragment; +import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment; +import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment; +import com.android.permissioncontroller.permission.ui.handheld.PermissionFooterPreference; +import com.android.permissioncontroller.permission.ui.handheld.PermissionPreference; +import com.android.permissioncontroller.permission.ui.handheld.PermissionPreferenceCategory; +import com.android.permissioncontroller.permission.ui.handheld.PermissionSwitchPreference; +import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader; +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; +import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs; +import com.android.permissioncontroller.permission.utils.KotlinUtils; +import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import kotlin.Pair; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Show and manage a single permission group for an app. + * + * <p>Allows the user to control whether the app is granted the permission. + * + * <p>Note: This fragment is opt-in on V and enabled on B. + */ +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +public class AppPermissionFragment extends SettingsWithLargeHeader + implements AppPermissionViewModel.ConfirmDialogShowingFragment { + private static final String LOG_TAG = "AppPermissionFragment"; + private static final long POST_DELAY_MS = 20; + private static final long EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS = 200L; + + private @NonNull AppPermissionViewModel mViewModel; + + private @NonNull PermissionPreferenceCategory mAppPermissionRationaleContainer; + private @NonNull PermissionPreference mAppPermissionRationaleContent; + private @NonNull PermissionPreferenceCategory mButtonCategory; + private @NonNull PermissionSelectorWithWidgetPreference mAllowButton; + private @NonNull SelectorWithWidgetPreference mAllowAlwaysButton; + private @NonNull SelectorWithWidgetPreference mAllowForegroundButton; + private @NonNull PermissionSelectorWithWidgetPreference mSelectButton; + private @NonNull SelectorWithWidgetPreference mAskOneTimeButton; + private @NonNull SelectorWithWidgetPreference mAskButton; + private @NonNull SelectorWithWidgetPreference mDenyButton; + private @NonNull SelectorWithWidgetPreference mDenyForegroundButton; + private @NonNull PermissionSwitchPreference mLocationAccuracySwitch; + private @NonNull PermissionTwoTargetPreference mDetails; + private @NonNull AppPermissionFooterLinkPreference mFooterLink1; + private @NonNull AppPermissionFooterLinkPreference mFooterLink2; + private @NonNull PermissionFooterPreference mFooterStorageSpecialAppAccess; + private @NonNull PermissionFooterPreference mAdditionalInfo; + + private @NonNull String mPackageName; + private @NonNull String mPermGroupName; + private @NonNull UserHandle mUser; + private boolean mIsStorageGroup; + private boolean mIsInitialLoad; + // This prevents the user from clicking the photo picker button multiple times in succession + private boolean mPhotoPickerTriggered; + private long mSessionId; + private String mPersistentDeviceId; + + private @NonNull String mPackageLabel; + private @NonNull String mPermGroupLabel; + private Drawable mPackageIcon; + private @NonNull RoleManager mRoleManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setHasOptionsMenu(true); + ActionBar ab = getActivity().getActionBar(); + if (ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + } + + mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); + mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME); + if (mPermGroupName == null) { + mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); + } + mIsStorageGroup = Objects.equals(mPermGroupName, STORAGE); + mUser = getArguments().getParcelable(Intent.EXTRA_USER); + mPackageLabel = BidiFormatter.getInstance().unicodeWrap( + KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName, + mUser)); + mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), + mPermGroupName).toString(); + mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(), + mPackageName, mUser); + mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); + + mPersistentDeviceId = getArguments().getString(PERSISTENT_DEVICE_ID, + MultiDeviceUtils.getDefaultDevicePersistentDeviceId()); + + AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory( + getActivity().getApplication(), mPackageName, mPermGroupName, mUser, mSessionId, + mPersistentDeviceId); + mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class); + Handler delayHandler = new Handler(Looper.getMainLooper()); + mViewModel.getButtonStateLiveData().observe(this, buttonState -> { + if (mIsInitialLoad) { + setRadioButtonsState(buttonState); + } else { + delayHandler.removeCallbacksAndMessages(null); + delayHandler.postDelayed(() -> setRadioButtonsState(buttonState), POST_DELAY_MS); + } + }); + mViewModel.getDetailResIdLiveData().observe(this, this::setDetail); + mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail); + if (mIsStorageGroup) { + mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState); + } + mViewModel.getShowPermissionRationaleLiveData().observe(this, + this::showPermissionRationaleDialog); + + mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class); + } + + @Override + public void onCreatePreferences(Bundle bundle, String s) { + super.onCreatePreferences(bundle, s); + addPreferencesFromResource(R.xml.app_permission); + + mAppPermissionRationaleContainer = requirePreference("app_permission_rationale_container"); + mAppPermissionRationaleContent = requirePreference("app_permission_rationale"); + mButtonCategory = requirePreference("app_permission_button_category"); + mAllowButton = requirePreference("app_permission_allow_radio_button"); + mAllowAlwaysButton = requirePreference("app_permission_allow_always_radio_button"); + mAllowForegroundButton = + requirePreference("app_permission_allow_foreground_only_radio_button"); + mSelectButton = requirePreference("app_permission_select_photos_radio_button"); + mAskOneTimeButton = requirePreference("app_permission_ask_one_time_radio_button"); + mAskButton = requirePreference("app_permission_ask_radio_button"); + mDenyButton = requirePreference("app_permission_deny_radio_button"); + mDenyForegroundButton = requirePreference("app_permission_deny_foreground_radio_button"); + mLocationAccuracySwitch = requirePreference("app_permission_location_accuracy_switch"); + mDetails = requirePreference("app_permission_details"); + mFooterLink1 = requirePreference("app_permission_footer_link_1"); + mFooterLink2 = requirePreference("app_permission_footer_link_2"); + mFooterStorageSpecialAppAccess = + requirePreference("app_permission_footer_storage_special_app_access"); + mAdditionalInfo = requirePreference("app_permission_additional_info"); + } + + @SuppressWarnings("TypeParameterUnusedInFormals") + private <T extends Preference> @NonNull T requirePreference(@NonNull CharSequence key) { + return Objects.requireNonNull(findPreference(key), + "Failed to find preference '" + key + "'"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + Context context = getContext(); + + mIsInitialLoad = true; + + setHeader(mPackageIcon, mPackageLabel, null, null, false); + + String text = null; + if (MultiDeviceUtils.isDefaultDeviceId(mPersistentDeviceId)) { + text = context.getString(R.string.app_permission_header, mPermGroupLabel); + } else { + final String deviceName = MultiDeviceUtils.getDeviceName(context, mPersistentDeviceId); + text = context.getString(R.string.app_permission_header_with_device_name, + mPermGroupLabel, deviceName); + } + mButtonCategory.setTitle(text); + + mFooterLink1.setSummary(context.getString( + R.string.app_permission_footer_app_permissions_link, mPackageLabel)); + String caller = getArguments().getString(EXTRA_CALLER_NAME); + setBottomLinkState(mFooterLink1, caller, Intent.ACTION_MANAGE_APP_PERMISSIONS); + + setBottomLinkState(mFooterLink2, caller, Intent.ACTION_MANAGE_PERMISSION_APPS); + + Set<String> exemptedPackages = Utils.getExemptedPackages(mRoleManager); + if (exemptedPackages.contains(mPackageName)) { + int additional_info_label = Utils.isStatusBarIndicatorPermission(mPermGroupName) + ? R.string.exempt_mic_camera_info_label : R.string.exempt_info_label; + mAdditionalInfo.setTitle(context.getString(additional_info_label, mPackageLabel)); + mAdditionalInfo.setVisible(true); + } else { + mAdditionalInfo.setVisible(false); + } + + if (mViewModel.getButtonStateLiveData().getValue() != null) { + setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); + } else { + mAllowButton.setVisible(false); + mAllowAlwaysButton.setVisible(false); + mAllowForegroundButton.setVisible(false); + mAskOneTimeButton.setVisible(false); + mAskButton.setVisible(false); + mDenyButton.setVisible(false); + mDenyForegroundButton.setVisible(false); + mLocationAccuracySwitch.setVisible(false); + mSelectButton.setVisible(false); + mSelectButton.setExtraWidgetOnClickListener(null); + } + + if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) { + setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue()); + } else { + mFooterStorageSpecialAppAccess.setVisible(false); + } + + // Avoid an animation by showing this Preference immediately + showPermissionRationaleDialog(mViewModel.getShowPermissionRationaleLiveData().getValue()); + + getActivity().setTitle( + getPreferenceManager().getContext().getString(R.string.app_permission_title, + mPermGroupLabel)); + + return super.onCreateView(inflater, container, savedInstanceState); + } + + public void onResume() { + super.onResume(); + // If we're returning to the fragment, photo picker hasn't been triggered + mPhotoPickerTriggered = false; + } + + private void showPermissionRationaleDialog(Boolean showPermissionRationale) { + showPermissionRationaleDialog(showPermissionRationale == Boolean.TRUE); + } + + private void showPermissionRationaleDialog(boolean showPermissionRationale) { + if (!showPermissionRationale) { + mAppPermissionRationaleContainer.setVisible(false); + } else { + mAppPermissionRationaleContainer.setVisible(true); + mAppPermissionRationaleContent.setOnPreferenceClickListener((v) -> { + if (SdkLevel.isAtLeastU()) { + mViewModel.showPermissionRationaleActivity(getActivity(), mPermGroupName); + } + return true; + }); + } + } + + private void setBottomLinkState(Preference preference, String caller, String action) { + if ((Objects.equals(caller, AppPermissionGroupsFragment.class.getName()) + && action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) + || (Objects.equals(caller, PermissionAppsFragment.class.getName()) + && action.equals(Intent.ACTION_MANAGE_PERMISSION_APPS))) { + preference.setVisible(false); + } else { + preference.setOnPreferenceClickListener((v) -> { + Bundle args; + if (action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) { + args = AppPermissionGroupsFragment.createArgs(mPackageName, mUser, + mSessionId, true); + } else { + args = PermissionAppsFragment.createArgs(mPermGroupName, mSessionId); + } + mViewModel.showBottomLinkPage(this, action, args); + return true; + }); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + pressBack(this); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void setRadioButtonsState(Map<ButtonType, ButtonState> states) { + if (states == null && !mViewModel.getButtonStateLiveData().isStale()) { + pressBack(this); + Log.w(LOG_TAG, "invalid package " + mPackageName + " or perm group " + + mPermGroupName); + Toast.makeText( + getActivity(), R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show(); + return; + } else if (states == null) { + return; + } + + mAllowButton.setOnClickListener((v) -> { + allowButtonFrameClickListener(); + }); + mAllowButton.setOnDisabledClickListener((v) -> { + allowButtonFrameClickListener(); + }); + mAllowAlwaysButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ALLOW_ALWAYS); + if (mIsStorageGroup) { + showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS, + R.string.special_file_access_dialog, -1, false); + } else { + mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS); + } + setResult(GRANTED_ALWAYS); + }); + mAllowForegroundButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ALLOW_FOREGROUND); + if (mIsStorageGroup) { + mViewModel.setAllFilesAccess(false); + mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); + setResult(GRANTED_ALWAYS); + } else { + mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND_ONLY, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND); + setResult(GRANTED_FOREGROUND_ONLY); + } + }); + mAskOneTimeButton.setOnClickListener((v) -> { + // mAskOneTimeButton only shows if checked hence should do nothing + markSingleButtonAsChecked(ButtonType.ASK_ONCE); + }); + mAskButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.ASK); + mViewModel.requestChange(true, this, this, ChangeRequest.REVOKE_BOTH, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME); + setResult(DENIED); + }); + mSelectButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.SELECT_PHOTOS); + int buttonPressed = + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED; + mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED, + buttonPressed); + }); + + if (isButtonChecked(states, ButtonType.SELECT_PHOTOS)) { + // Show the clickable extra widget + // TODO(b/366269715): make the widget show/hide via animation + mSelectButton.setExtraWidgetOnClickListener((v) -> { + if (isButtonChecked(states, ButtonType.SELECT_PHOTOS) && !mPhotoPickerTriggered) { + mPhotoPickerTriggered = true; + mViewModel.openPhotoPicker(this); + } + }); + } else { + // Hide the clickable extra widget + mSelectButton.setExtraWidgetOnClickListener(null); + } + + mDenyButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.DENY); + if (mViewModel.getFullStorageStateLiveData().getValue() != null + && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) { + mViewModel.setAllFilesAccess(false); + } + mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_BOTH, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY); + setResult(DENIED_DO_NOT_ASK_AGAIN); + }); + mDenyForegroundButton.setOnClickListener((v) -> { + markSingleButtonAsChecked(ButtonType.DENY_FOREGROUND); + mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_FOREGROUND, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND); + setResult(DENIED_DO_NOT_ASK_AGAIN); + }); + // Set long variable names to new variables to bypass linter errors. + int grantFineLocation = + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION; + int revokeFineLocation = + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION; + + mLocationAccuracySwitch.setOnPreferenceChangeListener((pref, newValue) -> { + if ((Boolean) newValue) { + mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FINE_LOCATION, + grantFineLocation); + } else { + mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_FINE_LOCATION, + revokeFineLocation); + } + // Don't actually toggle the switch yet. (Allow livedata observer to do so.) + return false; + }); + + setButtonState(mAllowButton, states.get(ButtonType.ALLOW)); + setButtonState(mAllowAlwaysButton, states.get(ButtonType.ALLOW_ALWAYS)); + setButtonState(mAllowForegroundButton, states.get(ButtonType.ALLOW_FOREGROUND)); + setButtonState(mAskOneTimeButton, states.get(ButtonType.ASK_ONCE)); + 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.isVisible()) { + mAllowButton.setTitle(R.string.app_permission_button_always_allow_all); + } else { + mAllowButton.setTitle(R.string.app_permission_button_allow); + } + + setButtonState(mLocationAccuracySwitch, states.get(ButtonType.LOCATION_ACCURACY)); + + mIsInitialLoad = false; + + if (mViewModel.getFullStorageStateLiveData().isInitialized()) { + setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue()); + } + } + + private void allowButtonFrameClickListener() { + if (!mAllowButton.isEnabled()) { + mViewModel.handleDisabledAllowButton(this); + } else { + markSingleButtonAsChecked(ButtonType.ALLOW); + mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND, + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); + setResult(GRANTED_ALWAYS); + } + } + + private void setButtonState(TwoStatePreference button, + AppPermissionViewModel.ButtonState state) { + button.setVisible(state.isShown()); + button.setChecked(state.isChecked()); + button.setEnabled(state.isEnabled()); + } + + private boolean isButtonChecked(Map<ButtonType, ButtonState> state, ButtonType button) { + return state.containsKey(button) && state.get(button).isChecked(); + } + + private void setSpecialStorageState(FullStoragePackageState storageState) { + if (!mAllowButton.isVisible() || !mIsStorageGroup) { + mFooterStorageSpecialAppAccess.setVisible(false); + return; + } + + mAllowAlwaysButton.setTitle(R.string.app_permission_button_allow_all_files); + mAllowForegroundButton.setTitle(R.string.app_permission_button_allow_media_only); + + if (storageState == null) { + mFooterStorageSpecialAppAccess.setVisible(false); + return; + } + + if (storageState.isLegacy()) { + mAllowButton.setTitle(R.string.app_permission_button_allow_all_files); + mFooterStorageSpecialAppAccess.setVisible(false); + return; + } + + mFooterStorageSpecialAppAccess.setVisible(true); + } + + private void setResult(@GrantPermissionsViewHandler.Result int result) { + if (!mPackageName.equals( + getActivity().getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME))) { + return; + } + Intent intent = new Intent() + .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED, mPermGroupName) + .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result); + getActivity().setResult(RESULT_OK, intent); + } + + private void setDetail(Pair<Integer, Integer> detailResIds) { + if (detailResIds == null) { + mDetails.setOnSecondTargetClickListener(null); + return; + } + if (detailResIds.getSecond() != null) { + // If the permissions are individually controlled, also show a link to the page that + // lets you control them. + mDetails.setExtraWidgetIconRes(R.drawable.ic_settings); + Bundle args = AllAppPermissionsFragment.createArgs(mPackageName, mPermGroupName, mUser); + mDetails.setOnSecondTargetClickListener((v) -> + mViewModel.showAllPermissions(this, args)); + mDetails.setSummary(getPreferenceManager().getContext().getString( + detailResIds.getFirst(), detailResIds.getSecond())); + } else { + mDetails.setOnSecondTargetClickListener(null); + mDetails.setSummary(getPreferenceManager().getContext().getString( + detailResIds.getFirst())); + } + mDetails.setVisible(true); + } + + private void setAdminSupportDetail(EnforcedAdmin admin) { + if (admin != null) { + mDetails.setExtraWidgetIconRes(R.drawable.ic_info_outline); + mDetails.setOnSecondTargetClickListener((v) -> + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin)); + } else { + mDetails.setOnSecondTargetClickListener(null); + } + } + + /** + * Show a dialog that warns the users that they are about to revoke permissions that were + * granted by default, or that they are about to grant full file access to an app. + * + * + * The order of operation to revoke a permission granted by default is: + * 1. `showConfirmDialog` + * 1. [ConfirmDialog.onCreateDialog] + * 1. [AppPermissionViewModel.onDenyAnyWay] or [AppPermissionViewModel.onConfirmFileAccess] + * TODO: Remove once data can be passed between dialogs and fragments with nav component + * + * @param changeRequest Whether background or foreground should be changed + * @param messageId The Id of the string message to show + * @param buttonPressed Button which was pressed to initiate the dialog, one of + * AppPermissionFragmentActionReported.button_pressed constants + * @param oneTime Whether the one-time (ask) button was clicked rather than the deny + * button + */ + @Override + public void showConfirmDialog(ChangeRequest changeRequest, @StringRes int messageId, + int buttonPressed, boolean oneTime) { + Bundle args = getArguments().deepCopy(); + args.putInt(ConfirmDialog.MSG, messageId); + args.putSerializable(ConfirmDialog.CHANGE_REQUEST, changeRequest); + args.putInt(ConfirmDialog.BUTTON, buttonPressed); + args.putBoolean(ConfirmDialog.ONE_TIME, oneTime); + ConfirmDialog defaultDenyDialog = new ConfirmDialog(); + defaultDenyDialog.setCancelable(true); + defaultDenyDialog.setArguments(args); + defaultDenyDialog.show(getChildFragmentManager().beginTransaction(), + ConfirmDialog.class.getName()); + } + + private void markSingleButtonAsChecked(ButtonType buttonToMarkChecked) { + if (!mViewModel.getButtonStateLiveData().isInitialized()) { + return; + } + Map<ButtonType, ButtonState> currentStates = mViewModel.getButtonStateLiveData().getValue(); + Map<ButtonType, ButtonState> newStates = new ArrayMap<>(); + for (ButtonType button: currentStates.keySet()) { + ButtonState state = currentStates.get(button); + boolean isChecked = button == buttonToMarkChecked + // Don't uncheck the Location Accuracy switch, if it is checked + || (button == ButtonType.LOCATION_ACCURACY && state.isChecked()); + ButtonState newState = new ButtonState(isChecked, state.isEnabled(), state.isShown(), + state.getCustomRequest()); + newStates.put(button, newState); + } + setRadioButtonsState(newStates); + } + + /** + * A dialog warning the user that they are about to deny a permission that was granted by + * default, or that they are denying a permission on a Pre-M app + * + * @see AppPermissionViewModel.ConfirmDialogShowingFragment#showConfirmDialog(ChangeRequest, + * int, int, boolean) + * @see #showConfirmDialog(ChangeRequest, int, int) + */ + public static class ConfirmDialog extends DialogFragment { + static final String MSG = ConfirmDialog.class.getName() + ".arg.msg"; + static final String CHANGE_REQUEST = ConfirmDialog.class.getName() + + ".arg.changeRequest"; + private static final String KEY = ConfirmDialog.class.getName() + ".arg.key"; + private static final String BUTTON = ConfirmDialog.class.getName() + ".arg.button"; + private static final String ONE_TIME = ConfirmDialog.class.getName() + ".arg.onetime"; + private static int sCode = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // TODO(b/229024576): This code is duplicated, refactor ConfirmDialog for easier + // NFF sharing + AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); + boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) + == ChangeRequest.GRANT_ALL_FILE_ACCESS; + int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; + if (isGrantFileAccess) { + positiveButtonStringResId = R.string.grant_dialog_button_allow; + } + AlertDialog.Builder b = new AlertDialog.Builder(getContext()) + .setMessage(getArguments().getInt(MSG)) + .setNegativeButton(R.string.cancel, + (DialogInterface dialog, int which) -> dialog.cancel()) + .setPositiveButton(positiveButtonStringResId, + (DialogInterface dialog, int which) -> { + if (isGrantFileAccess) { + fragment.mViewModel.setAllFilesAccess(true); + fragment.mViewModel.requestChange(false, fragment, + fragment, ChangeRequest.GRANT_BOTH, sCode); + } else { + fragment.mViewModel.onDenyAnyWay((ChangeRequest) + getArguments().getSerializable(CHANGE_REQUEST), + getArguments().getInt(BUTTON), + getArguments().getBoolean(ONE_TIME)); + } + }); + Dialog d = b.create(); + d.setCanceledOnTouchOutside(true); + return d; + } + + @Override + public void onCancel(DialogInterface dialog) { + AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); + fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue()); + } + } + + @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + public void showAdvancedConfirmDialog(AdvancedConfirmDialogArgs args) { + AlertDialog.Builder b = new AlertDialog.Builder(getContext()) + .setIcon(args.getIconId()) + .setMessage(args.getMessageId()) + .setOnCancelListener((DialogInterface dialog) -> { + setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); + }) + .setNegativeButton(args.getNegativeButtonTextId(), + (DialogInterface dialog, int which) -> { + setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); + }) + .setPositiveButton(args.getPositiveButtonTextId(), + (DialogInterface dialog, int which) -> { + mViewModel.requestChange(args.getSetOneTime(), + AppPermissionFragment.this, AppPermissionFragment.this, + args.getChangeRequest(), args.getButtonClicked()); + }); + if (args.getTitleId() != 0) { + b.setTitle(args.getTitleId()); + } + b.show(); + } +} +// LINT.ThenChange(../max35/LegacyAppPermissionFragment.java) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt new file mode 100644 index 000000000..1574eaba3 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionSelectorWithWidgetPreference.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld.v36 + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.widget.ImageView +import androidx.annotation.AttrRes +import androidx.annotation.DrawableRes +import androidx.annotation.IdRes +import androidx.annotation.RequiresApi +import androidx.preference.PreferenceViewHolder +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.utils.ResourceUtils +import com.android.settingslib.widget.SelectorWithWidgetPreference + +/** + * A `SelectorWithWidgetPreference` with additional features: + * - Propagates the supplied `app:extraWidgetIcon` drawable to the extraWidget + * - Propagates the supplied `app:extraWidgetId` id to the extraWidget (the icon on the right) + * - Propagates the supplied `app:checkboxId` id to the checkbox (or radio button, on the left) + * - Allows defining a "disabled click listener" handler that handles clicks when disabled + */ +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +class PermissionSelectorWithWidgetPreference : SelectorWithWidgetPreference { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context, attrs) + } + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + constructor(context: Context, isCheckbox: Boolean) : super(context, isCheckbox) { + init(context, null) + } + + private fun init(context: Context, attrs: AttributeSet?) { + layoutResource = R.layout.permission_preference_selector_with_widget + widgetLayoutResource = R.layout.permission_preference_widget_radiobutton + extraWidgetIconRes = + ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetIcon) + extraWidgetIdRes = ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetId) + checkboxIdRes = ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.checkboxId) + } + + @DrawableRes private var extraWidgetIconRes = 0 + @IdRes private var extraWidgetIdRes = 0 + @IdRes private var checkboxIdRes = 0 + private var onDisabledClickListener: OnClickListener? = null + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + val extraWidget = + holder.findViewById( + com.android.settingslib.widget.preference.selector.R.id.selector_extra_widget + ) as? ImageView + val checkbox = holder.findViewById(android.R.id.checkbox) + if (extraWidgetIconRes != 0) { + extraWidget?.setImageResource(extraWidgetIconRes) + } + if (extraWidgetIdRes != 0) { + extraWidget?.id = extraWidgetIdRes + } + if (checkboxIdRes != 0) { + checkbox?.id = checkboxIdRes + } + if (onDisabledClickListener != null) { + holder.itemView.isEnabled = true + holder.itemView.setOnClickListener { + onDisabledClickListener?.onRadioButtonClicked(this) + } + } + } + + fun setOnDisabledClickListener(onClickListener: OnClickListener?) { + onDisabledClickListener = onClickListener + notifyChanged() + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt new file mode 100644 index 000000000..cf6585f4d --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/PermissionTwoTargetPreference.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.handheld.v36 + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.widget.ImageView +import androidx.annotation.AttrRes +import androidx.annotation.DrawableRes +import androidx.annotation.RequiresApi +import androidx.annotation.StyleRes +import androidx.preference.PreferenceViewHolder +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.utils.ResourceUtils +import com.android.settingslib.widget.TwoTargetPreference + +/** + * A `TwoTargetPreference` with additional features: + * - Propagates the supplied `app:extraWidgetIcon` drawable to the second target + * - Allows defining a click listener on the second target (the icon on the right) + */ +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +class PermissionTwoTargetPreference : TwoTargetPreference { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context, attrs) + } + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + constructor( + context: Context, + attrs: AttributeSet?, + @AttrRes defStyleAttr: Int, + @StyleRes defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + layoutResource = R.layout.permission_preference_two_target + extraWidgetIconRes = + ResourceUtils.getResourceIdByAttr(context, attrs, R.attr.extraWidgetIcon) + } + + @DrawableRes private var extraWidgetIconRes = 0 + private var secondTargetClickListener: OnSecondTargetClickListener? = null + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + val settingsButton = holder.findViewById(R.id.settings_button) as ImageView + if (extraWidgetIconRes != 0) { + settingsButton.setImageResource(extraWidgetIconRes) + } + if (secondTargetClickListener != null) { + settingsButton.setOnClickListener { + secondTargetClickListener!!.onSecondTargetClick(this) + } + } else { + settingsButton.setOnClickListener(null) + } + } + + override fun getSecondTargetResId() = R.layout.settings_button_preference_widget + + override fun shouldHideSecondTarget() = secondTargetClickListener == null + + fun setOnSecondTargetClickListener(listener: OnSecondTargetClickListener?) { + secondTargetClickListener = listener + notifyChanged() + } + + fun setExtraWidgetIconRes(@DrawableRes extraWidgetIconRes: Int) { + this.extraWidgetIconRes = extraWidgetIconRes + notifyChanged() + } + + interface OnSecondTargetClickListener { + fun onSecondTargetClick(preference: TwoTargetPreference) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java index 0ec003cb1..5ca71b902 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java @@ -36,6 +36,7 @@ import androidx.fragment.app.FragmentActivity; import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.permission.compat.AppPermissionFragmentCompat; import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; import com.android.permissioncontroller.permission.ui.auto.AutoAppPermissionFragment; @@ -163,8 +164,8 @@ public final class AppPermissionActivity extends FragmentActivity { } else if (DeviceUtils.isTelevision(this)) { Fragment androidXFragment = new AppPermissionFragment(); androidXFragment.setArguments( - AppPermissionFragment.createArgs( - packageName, permissionName, groupName, userHandle, null, 0, null)); + AppPermissionFragmentCompat.createArgs(packageName, permissionName, groupName, + userHandle, null, 0, null, null)); getSupportFragmentManager().beginTransaction() .replace(android.R.id.content, androidXFragment) .commit(); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt deleted file mode 100644 index c782a85f8..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -@file:Suppress("DEPRECATION") - -package com.android.permissioncontroller.permission.ui.legacy - -import android.Manifest -import android.app.AppOpsManager -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 androidx.annotation.RequiresApi -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.R -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.TimelineUsage -import com.android.permissioncontroller.permission.model.v31.PermissionUsages -import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr -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.Utils -import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils -import java.time.Instant -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeUnit.DAYS -import kotlin.math.max - -/** View model for the permission details fragment. */ -@RequiresApi(Build.VERSION_CODES.S) -class PermissionUsageDetailsViewModelLegacy( - val application: Application, - val roleManager: RoleManager, - 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_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 mTimeFilterItemMs = mutableListOf<TimeFilterItemMs>() - - init { - initializeTimeFilterItems(application) - } - - /** Loads permission usages using [PermissionUsages]. Response is returned to the [callback]. */ - fun loadPermissionUsages( - loaderManager: LoaderManager, - permissionUsages: PermissionUsages, - callback: PermissionUsages.PermissionsUsagesChangeCallback, - filterTimesIndex: Int - ) { - val timeFilterItemMs: TimeFilterItemMs = mTimeFilterItemMs[filterTimesIndex] - val filterTimeBeginMillis = max(System.currentTimeMillis() - timeFilterItemMs.timeMs, 0) - permissionUsages.load( - /* filterPackageName= */ null, - /* filterPermissionGroups= */ null, - filterTimeBeginMillis, - Long.MAX_VALUE, - PermissionUsages.USAGE_FLAG_LAST or PermissionUsages.USAGE_FLAG_HISTORICAL, - loaderManager, - /* getUiInfo= */ false, - /* getNonPlatformPermissions= */ false, - /* callback= */ callback, - /* sync= */ false - ) - } - - /** - * Create a [PermissionUsageDetailsUiData] based on the provided data. - * - * @param appPermissionUsages data about app permission usages - * @param showSystem whether system apps should be shown - * @param show7Days whether the last 7 days of history should be shown - */ - fun buildPermissionUsageDetailsUiData( - appPermissionUsages: List<AppPermissionUsage>, - showSystem: Boolean, - show7Days: Boolean - ): PermissionUsageDetailsUiData { - val showPermissionUsagesDuration = - if (KotlinUtils.is7DayToggleEnabled() && show7Days) { - TIME_7_DAYS_DURATION - } else { - TIME_24_HOURS_DURATION - } - val startTime = - (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast( - Instant.EPOCH.toEpochMilli() - ) - val appPermissionTimelineUsages: List<AppPermissionTimelineUsage> = - 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 - ) - } - - 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.appPermissionTimelineUsage.permissionApp.uid - ), - discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.packageName, - discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.icon, - discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.label, - permissionGroup, - discreteAccessClusterData.discreteAccessDataList.last().accessTimeMs, - discreteAccessClusterData.discreteAccessDataList.first().accessTimeMs, - summary, - showingSubattribution, - discreteAccessClusterData.appPermissionTimelineUsage.attributionTags, - sessionId - ) - } - - /** - * 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 [AppPermissionTimelineUsage] for a particular permission group. */ - private fun extractAppPermissionTimelineUsagesForGroup( - appPermissionUsages: List<AppPermissionUsage>, - group: String - ): List<AppPermissionTimelineUsage> { - val exemptedPackages = Utils.getExemptedPackages(roleManager) - return appPermissionUsages.filter { !exemptedPackages.contains(it.packageName) } - .map { appPermissionUsage -> - getAppPermissionTimelineUsages( - appPermissionUsage.app, - appPermissionUsage.groupUsages.firstOrNull { it.group.name == group } - ) - } - .flatten() - } - - /** Returns whether the show/hide system toggle should be displayed in the UI. */ - private fun shouldDisplayShowSystemToggle( - appPermissionTimelineUsages: List<AppPermissionTimelineUsage>, - ): Boolean = - appPermissionTimelineUsages - .map { it.timelineUsage } - .filter { it.hasDiscreteData() } - .any { it.group.isSystem() } - - /** - * Returns a list of [PermissionApp] instances which had recent discrete permission usage - * (recent here refers to usages occurring after the provided start time). - */ - private fun getPermissionAppsWithRecentDiscreteUsage( - appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>, - showSystem: Boolean, - startTime: Long, - ): List<PermissionApp> = - appPermissionTimelineUsageList - .filter { it.timelineUsage.hasDiscreteData() } - .filter { showSystem || !it.timelineUsage.group.isSystem() } - .filter { it.timelineUsage.allDiscreteAccessTime.any { it.first >= startTime } } - .map { it.permissionApp } - - /** - * Builds a list of [DiscreteAccessClusterData] from the provided list of - * [AppPermissionTimelineUsage]. - */ - private fun buildDiscreteAccessClusterData( - appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>, - showSystem: Boolean, - startTime: Long, - ): List<DiscreteAccessClusterData> = - appPermissionTimelineUsageList - .map { appPermissionTimelineUsages -> - val accessDataList = - extractRecentDiscreteAccessData( - appPermissionTimelineUsages.timelineUsage, - showSystem, - startTime - ) - - if (accessDataList.size <= 1) { - return@map accessDataList.map { - DiscreteAccessClusterData(appPermissionTimelineUsages, listOf(it)) - } - } - - clusterDiscreteAccessData(appPermissionTimelineUsages, accessDataList) - } - .flatten() - .sortedWith( - compareBy( - { -it.discreteAccessDataList.first().accessTimeMs }, - { it.appPermissionTimelineUsage.permissionApp.label } - ) - ) - .toList() - - /** - * 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( - appPermissionTimelineUsage: AppPermissionTimelineUsage, - discreteAccessDataList: List<DiscreteAccessData> - ): List<DiscreteAccessClusterData> { - val clusterDataList = mutableListOf<DiscreteAccessClusterData>() - val currentDiscreteAccessDataList: MutableList<DiscreteAccessData> = mutableListOf() - for (discreteAccessData in discreteAccessDataList) { - if (currentDiscreteAccessDataList.isEmpty()) { - currentDiscreteAccessDataList.add(discreteAccessData) - } else if ( - !canAccessBeAddedToCluster(discreteAccessData, currentDiscreteAccessDataList) - ) { - clusterDataList.add( - DiscreteAccessClusterData( - appPermissionTimelineUsage, - currentDiscreteAccessDataList.toMutableList() - ) - ) - currentDiscreteAccessDataList.clear() - currentDiscreteAccessDataList.add(discreteAccessData) - } else { - currentDiscreteAccessDataList.add(discreteAccessData) - } - } - if (currentDiscreteAccessDataList.isNotEmpty()) { - clusterDataList.add( - DiscreteAccessClusterData(appPermissionTimelineUsage, currentDiscreteAccessDataList) - ) - } - return clusterDataList - } - - /** - * 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 extractRecentDiscreteAccessData( - timelineUsages: TimelineUsage, - showSystem: Boolean, - startTime: Long - ): List<DiscreteAccessData> { - return if ( - timelineUsages.hasDiscreteData() && (showSystem || !timelineUsages.group.isSystem()) - ) { - getRecentDiscreteAccessData(timelineUsages, startTime) - .sortedWith(compareBy { -it.accessTimeMs }) - .toList() - } else { - listOf() - } - } - - /** - * Extract recent [DiscreteAccessData] from a [TimelineUsage]. (recent here refers to accesses - * occurring after the provided start time). - */ - private fun getRecentDiscreteAccessData( - timelineUsage: TimelineUsage, - startTime: Long - ): List<DiscreteAccessData> { - return timelineUsage.allDiscreteAccessTime - .filter { it.first >= startTime } - .map { - DiscreteAccessData( - it.first, - it.second, - it.third, - ) - } - } - - /** - * 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. - * - * 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) - - /** 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.appPermissionTimelineUsage.label == Resources.ID_NULL) null - else - usage.appPermissionTimelineUsage.permissionApp.attributionLabels?.let { - it[usage.appPermissionTimelineUsage.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 - } - } - - /** - * Builds a list of [AppPermissionTimelineUsage] from the provided - * [AppPermissionUsage.GroupUsage]. - */ - private fun getAppPermissionTimelineUsages( - app: PermissionApp, - groupUsage: AppPermissionUsage.GroupUsage? - ): List<AppPermissionTimelineUsage> { - if (groupUsage == null) { - return listOf() - } - - if (shouldShowSubattributionForApp(app.appInfo)) { - return groupUsage.attributionLabelledGroupUsages.map { - AppPermissionTimelineUsage(permissionGroup, app, it, it.label) - } - } - - return listOf( - AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL) - ) - } - - /** 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 class HistoryPreferenceData( - val userHandle: UserHandle, - val pkgName: String, - val appIcon: Drawable?, - val preferenceTitle: String, - val permissionGroup: String, - val accessStartTime: Long, - val accessEndTime: Long, - val summaryText: CharSequence?, - val showingAttribution: Boolean, - val attributionTags: ArrayList<String>, - val sessionId: Long - ) - - /** - * A class representing a given time, e.g., "in the last hour". - * - * @param timeMs the time represented by this object in milliseconds. - * @param label the label to describe the timeframe - */ - 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@PermissionUsageDetailsViewModelLegacy.getHistoryPreferenceData(it) - } - } - } - - /** - * Data class representing a cluster of accesses, to be represented as a single entry in the UI. - */ - data class DiscreteAccessClusterData( - val appPermissionTimelineUsage: AppPermissionTimelineUsage, - val discreteAccessDataList: List<DiscreteAccessData> - ) - - /** 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 AppPermissionTimelineUsage( - /** 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 - /** App whose permissions are being tracked. */ - val permissionApp: PermissionApp, - /** Timeline usage for the given app and permission. */ - val timelineUsage: TimelineUsage, - val label: Int - ) { - val attributionTags: java.util.ArrayList<String> - get() = ArrayList(timelineUsage.attributionTags) - } -} - -/** Factory for an [PermissionUsageDetailsViewModelLegacy] */ -@RequiresApi(Build.VERSION_CODES.S) -class PermissionUsageDetailsViewModelFactoryLegacy( - private val application: Application, - private val roleManager: RoleManager, - private val filterGroup: String, - private val sessionId: Long -) : ViewModelProvider.Factory { - - override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") - return PermissionUsageDetailsViewModelLegacy( - application, - roleManager, - filterGroup, - sessionId - ) - as T - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt deleted file mode 100644 index 3032dece5..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -@file:Suppress("DEPRECATION") - -package com.android.permissioncontroller.permission.ui.legacy - -import android.Manifest -import android.app.LoaderManager -import android.app.role.RoleManager -import android.content.Context -import android.os.Build -import androidx.annotation.RequiresApi -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -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.utils.KotlinUtils -import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel -import com.android.permissioncontroller.permission.utils.PermissionMapping -import com.android.permissioncontroller.permission.utils.Utils -import java.time.Instant -import java.util.concurrent.TimeUnit -import kotlin.math.max - -/** [ViewModel] for Permission Usage fragments. */ -@RequiresApi(Build.VERSION_CODES.S) -class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() { - - private val exemptedPackages: Set<String> = Utils.getExemptedPackages(roleManager) - - /** Companion object for [PermissionUsageViewModelLegacy]. */ - 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, - Manifest.permission_group.CAMERA to 1, - Manifest.permission_group.MICROPHONE to 2 - ) - private const val DEFAULT_ORDER = 3 - } - - /** Loads data from [PermissionUsages] using the [LoaderManager] pattern. */ - fun loadPermissionUsages( - loaderManager: LoaderManager, - permissionUsages: PermissionUsages, - callback: PermissionUsages.PermissionsUsagesChangeCallback - ) { - val filterTimeBeginMillis = - max(System.currentTimeMillis() - TIME_FILTER_MILLIS, Instant.EPOCH.toEpochMilli()) - permissionUsages.load( - null /*filterPackageName*/, - null /*filterPermissionGroups*/, - filterTimeBeginMillis, - Long.MAX_VALUE, - PermissionUsages.USAGE_FLAG_LAST or PermissionUsages.USAGE_FLAG_HISTORICAL, - loaderManager, - false /*getUiInfo*/, - false /*getNonPlatformPermissions*/, - callback /*callback*/, - false /*sync*/ - ) - } - - /** - * Parses the provided list of [AppPermissionUsage] instances to build data for the UI to - * display. - */ - fun buildPermissionUsagesUiData( - appPermissionUsages: List<AppPermissionUsage>, - show7Days: Boolean, - showSystem: Boolean, - context: Context, - ): PermissionUsagesUiData { - val curTime = System.currentTimeMillis() - val showPermissionUsagesDuration = - if (KotlinUtils.is7DayToggleEnabled() && show7Days) { - TIME_7_DAYS_DURATION - } else { - TIME_24_HOURS_DURATION - } - val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli()) - - 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 - ) - } - - /** - * 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, - startTime: Long, - showSystem: Boolean - ): List<PermissionGroupWithUsageCount> { - val permissionGroupsUsageCountMap: MutableMap<String, Int> = HashMap() - extractPlatformAppPermissionGroupsToDisplay().forEach { - permissionGroupsUsageCountMap[it] = 0 - } - - 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 permissionGroupsUsageCountMap.entries - .map { PermissionGroupWithUsageCount(it.key, it.value) } - .sortedWith( - compareBy( - { PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) }, - { getPermGroupLabel(context, it.permGroup).toString() } - ) - ) - } - - /** 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 } - } - .map { it.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 List<AppPermissionUsage>.displayShowSystemToggle( - startTime: Long, - ): Boolean { - return flatMap { it.groupUsages } - .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) } - .filter { it.lastAccessTime > startTime && it.lastAccessTime > 0L } - .any { it.group.isSystem() } - } - - /** - * 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() - - /** - * 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>, - ) - - /** - * 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 [PermissionUsageViewModelLegacy]. */ -@RequiresApi(Build.VERSION_CODES.S) -class PermissionUsageViewModelFactoryLegacy(private val roleManager: RoleManager) : - ViewModelProvider.Factory { - - override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") return PermissionUsageViewModelLegacy(roleManager) as T - } -} 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 ee0c5d2f2..4f43f4a0e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest +import android.annotation.SuppressLint import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_IGNORED @@ -31,6 +32,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags +import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION @@ -51,7 +54,6 @@ import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGr import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category 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 @@ -128,6 +130,42 @@ class AppPermissionGroupsViewModel( private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData private val packagePermsExternalDeviceLiveData = PackagePermissionsExternalDeviceLiveData[packageName, user] + private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! + private val packageManager = app.packageManager + + /** Check if the application is in restricted settings mode. */ + @SuppressLint("NewApi") + fun isClearRestrictedAllowed(): Boolean { + if (Flags.enhancedConfirmationBackportEnabled()) { + // TODO(b/347876543): Replace this when EnhancedConfirmtionServiceImpl is + // available. + val isRestricted = + appOpsManager.noteOpNoThrow( + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + packageManager.getApplicationInfoAsUser(packageName, 0, user).uid, + packageName, + null, + null + ) == MODE_IGNORED + return isRestricted + } + return false + } + + /** Allow restricted settings on the applications. */ + @SuppressLint("NewApi") + fun clearRestriction() { + if (Flags.enhancedConfirmationBackportEnabled()) { + // TODO(b/347876543): Replace this when EnhancedConfirmationServiceImpl is + // available. + appOpsManager.setMode( + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + packageManager.getApplicationInfoAsUser(packageName, 0, user).uid, + packageName, + MODE_ALLOWED + ) + } + } /** * LiveData whose data is a map of grant category (either allowed or denied) to a list of @@ -396,7 +434,7 @@ class AppPermissionGroupsViewModel( } val aggregateDataFilterBeginDays = - if (KotlinUtils.is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + if (DeviceUtils.isHandheld()) 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 5b6d8b8db..8a9408517 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt @@ -127,6 +127,7 @@ class AppPermissionViewModel( oneTime: Boolean ) + @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun showAdvancedConfirmDialog(args: AdvancedConfirmDialogArgs) } @@ -325,6 +326,7 @@ class AppPermissionViewModel( private val mainLocListener = { isEnabled: Boolean -> checkAndUpdateStatus(!isEnabled) } private val locBypassListener = { _: Boolean -> checkAndUpdateStatus() } + override fun onUpdate() { checkAndUpdateStatus() } @@ -692,8 +694,7 @@ class AppPermissionViewModel( app, packageName, permGroupName - ) - ?: return + ) ?: return fragment.startActivity(restrictionIntent) } @@ -726,10 +727,8 @@ class AppPermissionViewModel( // 2. Else if FINE or COARSE have the isSelectedLocationAccuracy flag set, then return // true if FINE isSelectedLocationAccuracy is set. // 3. Else, return default precision from device config. - return if ( - fineLocation.isGrantedIncludingAppOp || coarseLocation.isGrantedIncludingAppOp - ) { - fineLocation.isGrantedIncludingAppOp + return if (fineLocation.isGranted || coarseLocation.isGranted) { + fineLocation.isGranted } else if ( fineLocation.isSelectedLocationAccuracy || coarseLocation.isSelectedLocationAccuracy ) { @@ -1381,9 +1380,7 @@ class AppPermissionViewModel( } private fun getIndividualPermissionDetailResId(group: LightAppPermGroup): Pair<Int, Int> { - return when ( - val numRevoked = group.permissions.filter { !it.value.isGrantedIncludingAppOp }.size - ) { + return when (val numRevoked = group.permissions.filter { !it.value.isGranted }.size) { 0 -> R.string.permission_revoked_none to numRevoked group.permissions.size -> R.string.permission_revoked_all to numRevoked else -> R.string.permission_revoked_count to numRevoked @@ -1454,7 +1451,7 @@ class AppPermissionViewModel( val newPermission = newGroup.permissions[permName] ?: continue if ( - permission.isGrantedIncludingAppOp != newPermission.isGrantedIncludingAppOp || + permission.isGranted != newPermission.isGranted || permission.flags != newPermission.flags ) { logAppPermissionFragmentActionReported(changeId, newPermission, buttonPressed) @@ -1462,7 +1459,7 @@ class AppPermissionViewModel( app.applicationContext, packageName, permGroupName, - newPermission.isGrantedIncludingAppOp + newPermission.isGranted ) PermissionChangeStorageImpl.recordPermissionChange(packageName) } @@ -1492,7 +1489,7 @@ class AppPermissionViewModel( uid, packageName, permission.permInfo.name, - permission.isGrantedIncludingAppOp, + permission.isGranted, permission.flags, buttonPressed ) @@ -1502,7 +1499,7 @@ class AppPermissionViewModel( "$changeId uid=$uid packageName=$packageName permission=" + permission.permInfo.name + " isGranted=" + - permission.isGrantedIncludingAppOp + + permission.isGranted + " permissionFlags=" + permission.flags + " buttonPressed=$buttonPressed" @@ -1544,7 +1541,7 @@ class AppPermissionViewModel( return group.isGranted && group.permissions.values.all { - it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp) + it.name in partialPerms || (it.name !in partialPerms && !it.isGranted) } } } @@ -1559,28 +1556,16 @@ class AppPermissionViewModel( * @param sessionId A session ID used in logs to identify this particular session * @param persistentDeviceId Indicates the device in the context of virtual devices */ -class AppPermissionViewModelFactory( +class AppPermissionViewModelFactory +@JvmOverloads +constructor( private val app: Application, private val packageName: String, private val permGroupName: String, private val user: UserHandle, private val sessionId: Long, - private val persistentDeviceId: String + private val persistentDeviceId: String = MultiDeviceUtils.getDefaultDevicePersistentDeviceId() ) : ViewModelProvider.Factory { - constructor( - app: Application, - packageName: String, - permGroupName: String, - user: UserHandle, - sessionId: Long - ) : this( - app, - packageName, - permGroupName, - user, - sessionId, - MultiDeviceUtils.getDefaultDevicePersistentDeviceId() - ) override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") 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 b5df6f410..1e5b96c2e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -29,6 +29,7 @@ import android.annotation.SuppressLint import android.app.Activity import android.app.Application import android.app.admin.DevicePolicyManager +import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED @@ -41,6 +42,8 @@ import android.os.Build import android.os.Bundle import android.os.Process import android.permission.PermissionManager +import android.permission.flags.Flags +import android.util.ArrayMap import android.util.Log import androidx.core.util.Consumer import androidx.lifecycle.ViewModel @@ -116,6 +119,7 @@ import com.android.permissioncontroller.permission.utils.SafetyNetLogger import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils +import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils.isPermissionDeviceAware /** * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by the @@ -153,6 +157,21 @@ class GrantPermissionsViewModel( } else { null } + private val permissionGroupToDeviceIdMap: Map<String, Int> = + if (SdkLevel.isAtLeastV() && Flags.allowHostPermissionDialogsOnVirtualDevices()) { + requestedPermissions + .filter({ PermissionMapping.getGroupOfPlatformPermission(it) != null }) + .associateBy({ PermissionMapping.getGroupOfPlatformPermission(it)!! }, { + if (isPermissionDeviceAware( + app.applicationContext, + deviceId, + it + ) + ) deviceId else Context.DEVICE_ID_DEFAULT + }) + } else { + ArrayMap() + } private val dpm = app.getSystemService(DevicePolicyManager::class.java)!! private val permissionPolicy = dpm.getPermissionPolicy(null) private val groupStates = mutableMapOf<String, GroupState>() @@ -314,7 +333,8 @@ class GrantPermissionsViewModel( } val getLiveDataFun = { groupName: String -> - LightAppPermGroupLiveData[packageName, groupName, user, deviceId] + LightAppPermGroupLiveData[packageName, groupName, user, + permissionGroupToDeviceIdMap.get(groupName) ?: deviceId] } setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun) } @@ -338,7 +358,7 @@ class GrantPermissionsViewModel( if (state != null) { val allAffectedGranted = state.affectedPermissions.all { perm -> - appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true && + appPermGroup.permissions[perm]?.isGranted == true && appPermGroup.permissions[perm]?.isRevokeWhenRequested == false } if (allAffectedGranted) { @@ -398,7 +418,8 @@ class GrantPermissionsViewModel( safetyLabel, groupState.group.permGroupName ), - deviceId + permissionGroupToDeviceIdMap.get(groupState.group.permGroupName) + ?: deviceId ) ) } @@ -592,7 +613,7 @@ class GrantPermissionsViewModel( val behavior = getGrantBehavior(group) return if (behavior.isGroupFullyGranted(group, groupRequestedPermissions)) { - if (group.permissions[perm]?.isGrantedIncludingAppOp == false) { + if (group.permissions[perm]?.isGranted == false) { if (isBackground) { grantBackgroundRuntimePermissions(app, group, listOf(perm)) } else { @@ -864,18 +885,15 @@ class GrantPermissionsViewModel( } else { PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED } + var affectedPermissions: Collection<String> = groupState.affectedPermissions if (shouldAffectBackgroundPermissions) { - grantBackgroundRuntimePermissions( - app, - groupState.group, - groupState.affectedPermissions - ) + grantBackgroundRuntimePermissions(app, groupState.group, affectedPermissions) } else if (shouldAffectForegroundPermssions) { if (affectedForegroundPermissions == null) { grantForegroundRuntimePermissions( app, groupState.group, - groupState.affectedPermissions, + affectedPermissions, isOneTime ) // This prevents weird flag state when app targetSDK switches from S+ to R- @@ -883,11 +901,12 @@ class GrantPermissionsViewModel( KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, groupState.group, true) } } else { + affectedPermissions = affectedForegroundPermissions val newGroup = grantForegroundRuntimePermissions( app, groupState.group, - affectedForegroundPermissions, + affectedPermissions, isOneTime ) if (!isOneTime || newGroup.isOneTime) { @@ -899,7 +918,17 @@ class GrantPermissionsViewModel( } } } - groupState.state = STATE_GRANTED + val shouldDenyFullGroupGrant = + groupState.group.isPlatformPermissionGroup && + affectedPermissions.none { + groupState.group.permissions[it]?.isPlatformOrSystem == true + } + groupState.state = + if (shouldDenyFullGroupGrant) { + STATE_UNKNOWN + } else { + STATE_GRANTED + } } else { if (shouldAffectBackgroundPermissions) { revokeBackgroundRuntimePermissions( diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt index bd80a88cd..429799157 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt @@ -16,17 +16,23 @@ package com.android.permissioncontroller.permission.ui.model +import android.Manifest import android.app.Application +import android.content.Intent +import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Bundle import androidx.fragment.app.Fragment import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController +import com.android.permission.flags.Flags import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.data.PermGroupsPackagesLiveData import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData +import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.navigateSafe /** @@ -38,6 +44,12 @@ import com.android.permissioncontroller.permission.utils.navigateSafe class ManageCustomPermissionsViewModel(private val app: Application) : AndroidViewModel(app) { val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData()) + val additionaPermGroupsUiInfo = + PermGroupsPackagesUiInfoLiveData( + app, + if (Flags.declutteredPermissionManagerEnabled()) AdditionalPermGroupNamesLiveData(app) + else MutableLiveData<List<String>>(), + ) /** * Navigate to a Permission Apps fragment @@ -46,6 +58,15 @@ class ManageCustomPermissionsViewModel(private val app: Application) : AndroidVi * @param args The args to pass to the new fragment */ fun showPermissionApps(fragment: Fragment, args: Bundle) { + val groupName = args.getString(Intent.EXTRA_PERMISSION_GROUP_NAME) + if (groupName == Manifest.permission_group.NOTIFICATIONS) { + 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) } } @@ -58,7 +79,8 @@ class ManageCustomPermissionsViewModel(private val app: Application) : AndroidVi class ManageCustomPermissionsViewModelFactory(private val app: Application) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") return ManageCustomPermissionsViewModel(app) as T + @Suppress("UNCHECKED_CAST") + return ManageCustomPermissionsViewModel(app) as T } } @@ -77,3 +99,32 @@ class UsedCustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String /* No op override */ } } + +/** + * A LiveData that is the union of LiveData UsedCustomPermGroupNamesLiveData and + * UnusedStandardPermGroupNamesLiveData. + * + * @param app The current application of the fragment + */ +class AdditionalPermGroupNamesLiveData(private val app: Application) : + SmartUpdateMediatorLiveData<List<String>>() { + + val usedCustomGroupNames = UsedCustomPermGroupNamesLiveData() + val unusedStandardGroupNames = UnusedStandardPermGroupNamesLiveData(app) + + init { + addSource(usedCustomGroupNames) { update() } + addSource(unusedStandardGroupNames) { update() } + } + + private fun combineGroupNames( + groupNames1: List<String>?, + groupNames2: List<String>?, + ): List<String> { + return (groupNames1 ?: emptyList()) + (groupNames2 ?: emptyList()) + } + + override fun onUpdate() { + value = combineGroupNames(usedCustomGroupNames.value, unusedStandardGroupNames.value) + } +} 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 aeab0aa89..6dabe8ab7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt @@ -23,8 +23,11 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Bundle import androidx.fragment.app.Fragment import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.map import androidx.navigation.fragment.findNavController +import com.android.permission.flags.Flags import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.data.PermGroupsPackagesLiveData import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData @@ -45,7 +48,21 @@ import com.android.permissioncontroller.permission.utils.navigateSafe class ManageStandardPermissionsViewModel(private val app: Application) : AndroidViewModel(app) { val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData) + val usedStandardPermGroupsUiInfo = + PermGroupsPackagesUiInfoLiveData( + app, + if (Flags.declutteredPermissionManagerEnabled()) UsedStandardPermGroupNamesLiveData(app) + else MutableLiveData<List<String>>(), + ) val numCustomPermGroups = NumCustomPermGroupsWithPackagesLiveData() + val numUnusedStandardPermGroups = + MediatorLiveData<Int>().apply { + if (Flags.declutteredPermissionManagerEnabled()) { + addSource(UnusedStandardPermGroupNamesLiveData(app)) { groupNames -> + value = groupNames.size + } + } + } val numAutoRevoked = unusedAutoRevokePackagesLiveData.map { it?.size ?: 0 } /** @@ -98,3 +115,47 @@ class NumCustomPermGroupsWithPackagesLiveData() : SmartUpdateMediatorLiveData<In value = customPermGroupPackages.value?.size ?: 0 } } + +/** + * A LiveData that tracks the names of the platform-defined permission groups, such that at least + * one of the permissions in the group has been requested at runtime by at least one non-system + * application. + * + * @param app The current application of the fragment + */ +class UsedStandardPermGroupNamesLiveData(private val app: Application) : + SmartUpdateMediatorLiveData<List<String>>() { + init { + addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) { + permGroups -> + if (permGroups.values.any { it != null }) { + value = + permGroups.filterValues { it != null && it.nonSystemTotal > 0 }.keys.toList() + } + } + } + + override fun onUpdate() { + /* No op override */ + } +} + +/** + * A LiveData that tracks the names of the platform-defined permission groups, such that none of the + * the permissions in the group has been requested at runtime by any non-system application. + * + * @param app The current application of the fragment + */ +class UnusedStandardPermGroupNamesLiveData(private val app: Application) : + SmartUpdateMediatorLiveData<List<String>>() { + init { + addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) { + permGroups -> + value = permGroups.filterValues { it != null && it.nonSystemTotal == 0 }.keys.toList() + } + } + + override fun onUpdate() { + /* No op override */ + } +} 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 9317e7577..de35339bf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt @@ -37,6 +37,7 @@ import androidx.navigation.fragment.findNavController import androidx.preference.Preference import androidx.savedstate.SavedStateRegistryOwner import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils 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__ALLOWED_FOREGROUND @@ -57,7 +58,6 @@ import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewMo 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 @@ -164,7 +164,8 @@ class PermissionAppsViewModel( inner class CategorizedAppsLiveData(groupName: String) : MediatorLiveData< - @kotlin.jvm.JvmSuppressWildcards Map<Category, List<Pair<String, UserHandle>>> + @kotlin.jvm.JvmSuppressWildcards + Map<Category, List<Pair<String, UserHandle>>> >() { private val packagesUiInfoLiveData = SinglePermGroupPackagesUiInfoLiveData[groupName] @@ -335,7 +336,7 @@ class PermissionAppsViewModel( fun getFilterTimeBeginMillis(): Long { val aggregateDataFilterBeginDays = - if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + if (DeviceUtils.isHandheld()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 return max( @@ -358,7 +359,7 @@ class PermissionAppsViewModel( } val aggregateDataFilterBeginDays = - if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + if (DeviceUtils.isHandheld()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 val now = System.currentTimeMillis() val filterTimeBeginMillis = 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 8613d1cae..ecb551ffc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt @@ -133,7 +133,7 @@ class ReviewPermissionsViewModel(val app: Application, val packageInfo: PackageI val lightPerms = permGroup.allPermissions.values.toList() val permissionCount = lightPerms.size for (i in 0 until permissionCount) { - if (!lightPerms[i].isGrantedIncludingAppOp) { + if (!lightPerms[i].isGranted) { revokedCount++ } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt index 6234b2755..7112d180f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt @@ -57,7 +57,7 @@ object BackgroundGrantBehavior : GrantBehavior() { val requestsBg = hasBgPerms(group, requestedPerms) val requestsFg = requestedPerms.any { it !in group.backgroundPermNames } val isOneTimeGroup = PermissionMapping.supportsOneTimeGrant(group.permGroupName) - val isFgGranted = group.foreground.isGrantedExcludingRWROrAllRWR + val isFgGranted = group.foreground.allowFullGroupGrant val isFgOneTime = group.foreground.isOneTime val splitSdk = getSdkGroupWasSplitToBg(requestedPerms) val isAppIsOlderThanSplitToBg = group.packageInfo.targetSdkVersion < splitSdk @@ -171,16 +171,15 @@ object BackgroundGrantBehavior : GrantBehavior() { group: LightAppPermGroup, requestedPerms: Set<String> ): Boolean { - return (!hasBgPerms(group, requestedPerms) || - group.background.isGrantedExcludingRWROrAllRWR) && - group.foreground.isGrantedExcludingRWROrAllRWR + return (!hasBgPerms(group, requestedPerms) || group.background.allowFullGroupGrant) && + group.foreground.allowFullGroupGrant } override fun isForegroundFullyGranted( group: LightAppPermGroup, requestedPerms: Set<String> ): Boolean { - return group.foreground.isGrantedExcludingRWROrAllRWR + return group.foreground.allowFullGroupGrant } override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt index 3b3619084..54d6dc0f3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt @@ -58,7 +58,7 @@ abstract class GrantBehavior { * group not already granted will be granted. */ open fun isGroupFullyGranted(group: LightAppPermGroup, requestedPerms: Set<String>): Boolean { - return group.foreground.isGrantedExcludingRWROrAllRWR + return group.foreground.allowFullGroupGrant } /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt index 8e540701a..4f5def312 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt @@ -47,7 +47,7 @@ object HealthGrantBehavior : GrantBehavior() { group: LightAppPermGroup, requestedPerms: Set<String> ): Boolean { - return requestedPerms.all { group.permissions[it]?.isGrantedIncludingAppOp != false } + return requestedPerms.all { group.permissions[it]?.isGranted != false } } override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt index 2c5e2b732..31ac04cab 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt @@ -37,8 +37,7 @@ object LocationGrantBehavior : GrantBehavior() { ): Prompt { val backgroundPrompt = BackgroundGrantBehavior.getPrompt(group, requestedPerms) val requestsBackground = requestedPerms.any { it in group.backgroundPermNames } - val coarseGranted = - group.permissions[ACCESS_COARSE_LOCATION]?.isGrantedIncludingAppOp == true + val coarseGranted = group.permissions[ACCESS_COARSE_LOCATION]?.isGranted == true return if (!supportsLocationAccuracy(group) || requestsBackground) { backgroundPrompt } else if (requestedPerms.contains(ACCESS_FINE_LOCATION)) { @@ -84,10 +83,10 @@ object LocationGrantBehavior : GrantBehavior() { } if (requestedPerms.contains(ACCESS_FINE_LOCATION)) { - return group.permissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == true + return group.permissions[ACCESS_FINE_LOCATION]?.isGranted == true } - return group.foreground.isGrantedExcludingRWROrAllRWR + return group.foreground.allowFullGroupGrant } override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt index a690f5f59..9dd87674a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt @@ -66,11 +66,11 @@ object StorageGrantBehavior : GrantBehavior() { } val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] - if (userSelectedPerm?.isUserFixed == true && userSelectedPerm.isGrantedIncludingAppOp) { + if (userSelectedPerm?.isUserFixed == true && userSelectedPerm.isGranted) { return Prompt.NO_UI_PHOTO_PICKER_REDIRECT } - if (userSelectedPerm?.isGrantedIncludingAppOp == true) { + if (userSelectedPerm?.isGranted == true) { return Prompt.SELECT_MORE_PHOTOS } else { return Prompt.SELECT_PHOTOS @@ -97,16 +97,14 @@ object StorageGrantBehavior : GrantBehavior() { } return group.permissions.values.any { - it.name !in getPartialGrantPermissions(group) && it.isGrantedIncludingAppOp + it.name !in getPartialGrantPermissions(group) && it.isGranted } } override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean { val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] if ( - userSelectedPerm != null && - userSelectedPerm.isGrantedIncludingAppOp && - userSelectedPerm.isUserFixed + userSelectedPerm != null && userSelectedPerm.isGranted && userSelectedPerm.isUserFixed ) { // If the user selected permission is fixed and granted, we immediately show the // photo picker, rather than filtering diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt new file mode 100644 index 000000000..64c5c6927 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/BasePermissionUsageDetailsViewModel.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.model.v31 + +import android.app.Application +import android.graphics.drawable.Drawable +import android.os.UserHandle +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.CLUSTER_SPACING_MINUTES +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState +import com.android.permissioncontroller.permission.utils.KotlinUtils +import java.util.concurrent.TimeUnit + +abstract class BasePermissionUsageDetailsViewModel(val app: Application) : AndroidViewModel(app) { + abstract fun getPermissionUsagesDetailsInfoUiLiveData(): LiveData<PermissionUsageDetailsUiState> + + abstract fun getShowSystem(): Boolean + + abstract val showSystemLiveData: LiveData<Boolean> + + abstract fun getShow7Days(): Boolean + + abstract fun updateShowSystemAppsToggle(showSystem: Boolean) + + abstract fun updateShow7DaysToggle(show7Days: Boolean) + + private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf() + private val packageLabelCache: MutableMap<String, String> = mutableMapOf() + + /** + * Returns the label for the provided package name, by first searching the cache otherwise + * retrieving it from the app's [android.content.pm.ApplicationInfo]. + */ + fun getPackageLabel(packageName: String, user: UserHandle): String { + if (packageLabelCache.containsKey(packageName)) { + return requireNotNull(packageLabelCache[packageName]) + } + val packageLabel = getPackageLabel(app, packageName, user) + packageLabelCache[packageName] = packageLabel + return packageLabel + } + + open fun getPackageLabel(app: Application, packageName: String, user: UserHandle): String { + return KotlinUtils.getPackageLabel(app, packageName, user) + } + + /** + * Returns the icon for the provided package name and user, by first searching the cache + * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo]. + */ + fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? { + val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle) + if (packageIconCache.containsKey(packageNameWithUser)) { + return requireNotNull(packageIconCache[packageNameWithUser]) + } + val packageIcon = getBadgedPackageIcon(app, packageName, userHandle) + if (packageIcon != null) packageIconCache[packageNameWithUser] = packageIcon + + return packageIcon + } + + open fun getBadgedPackageIcon( + app: Application, + packageName: String, + user: UserHandle + ): Drawable? { + return KotlinUtils.getBadgedPackageIcon(app, packageName, user) + } + + fun getDurationSummary(durationMs: Long): String? { + // 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(app, durationMs) + } + return null + } + + fun buildUsageSummary( + subAttributionLabel: String?, + proxyPackageLabel: String?, + durationSummary: String? + ): 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 -> + app.getString( + R.string.history_preference_subtext_3, + subTextStrings[0], + subTextStrings[1], + subTextStrings[2] + ) + 2 -> + app.getString( + R.string.history_preference_subtext_2, + subTextStrings[0], + subTextStrings[1] + ) + 1 -> subTextStrings[0] + else -> null + } + } +} 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 93fc280b4..497e5cc35 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 @@ -34,12 +34,16 @@ import android.os.Build import android.os.Bundle import android.os.UserHandle import android.os.UserManager +import android.permission.flags.Flags +import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.AbstractSavedStateViewModelFactory +import androidx.lifecycle.LiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.savedstate.SavedStateRegistryOwner import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.compat.IntentCompat import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData @@ -56,7 +60,6 @@ import com.android.permissioncontroller.permission.model.livedatatypes.v31.Light import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps.DiscreteAccess import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard -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.v31.SubattributionUtils @@ -68,10 +71,10 @@ import java.util.concurrent.TimeUnit.DAYS /** [ViewModel] for the Permission Usage Details page. */ @RequiresApi(Build.VERSION_CODES.S) class PermissionUsageDetailsViewModel( - val application: Application, + private val application: Application, private val state: SavedStateHandle, private val permissionGroup: String, -) : ViewModel() { +) : BasePermissionUsageDetailsViewModel(application) { val allLightHistoricalPackageOpsLiveData = AllLightHistoricalPackageOpsLiveData(application, opNames) @@ -79,11 +82,9 @@ class PermissionUsageDetailsViewModel( mutableMapOf<AppPermissionId, AppPermGroupUiInfoLiveData>() private val lightPackageInfoLiveDataMap = mutableMapOf<Pair<String, UserHandle>, LightPackageInfoLiveData>() - val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false) - val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false) - private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf() - private val packageLabelCache: MutableMap<String, String> = mutableMapOf() + override val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false) + val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false) private val roleManager = Utils.getSystemServiceSafe(application.applicationContext, RoleManager::class.java) @@ -91,25 +92,25 @@ class PermissionUsageDetailsViewModel( Utils.getSystemServiceSafe(application.applicationContext, UserManager::class.java) /** Updates whether system app permissions usage should be displayed in the UI. */ - fun updateShowSystemAppsToggle(showSystem: Boolean) { + override fun updateShowSystemAppsToggle(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 updateShow7DaysToggle(show7Days: Boolean) { + override fun updateShow7DaysToggle(show7Days: Boolean) { if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { state[SHOULD_SHOW_7_DAYS_KEY] = show7Days } } - /** Creates a [PermissionUsageDetailsUiInfo] containing all information to render the UI. */ - fun buildPermissionUsageDetailsUiInfo(): PermissionUsageDetailsUiInfo { + /** Creates a [PermissionUsageDetailsUiState] containing all information to render the UI. */ + fun buildPermissionUsageDetailsUiInfo(): PermissionUsageDetailsUiState { 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) { + if (show7Days && DeviceUtils.isHandheld()) { TIME_7_DAYS_DURATION } else { TIME_24_HOURS_DURATION @@ -119,15 +120,15 @@ class PermissionUsageDetailsViewModel( Instant.EPOCH.toEpochMilli() ) - return PermissionUsageDetailsUiInfo( - show7Days, - showSystem, + return PermissionUsageDetailsUiState.Success( buildAppPermissionAccessUiInfoList( allLightHistoricalPackageOpsLiveData, startTime, showSystem ), - containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime) + containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime), + showSystem, + show7Days ) } @@ -147,8 +148,7 @@ class PermissionUsageDetailsViewModel( .filterOutExemptAppPermissions(true) .filterAccessesLaterThan(startTime) } - ?.any { isAppPermissionSystem(it.appPermissionId) } - ?: false + ?.any { isAppPermissionSystem(it.appPermissionId) } ?: false } private fun isPermissionRequestedByApp(appPermissionId: AppPermissionId): Boolean { @@ -156,8 +156,15 @@ class PermissionUsageDetailsViewModel( lightPackageInfoLiveDataMap[ Pair(appPermissionId.packageName, appPermissionId.userHandle)] ?.value - ?.requestedPermissions - ?: listOf() + ?.requestedPermissions ?: listOf() + + if ( + appPermissionId.permissionGroup == Manifest.permission_group.LOCATION && + appRequestedPermissions.contains(Manifest.permission.LOCATION_BYPASS) + ) { + return true + } + return appRequestedPermissions.any { PermissionMapping.getGroupOfPlatformPermission(it) == appPermissionId.permissionGroup } @@ -197,9 +204,8 @@ class PermissionUsageDetailsViewModel( .getLightHistoricalPackageOps() ?.filter { Utils.shouldShowInSettings(it.userHandle, userManager) } ?.flatMap { it.clusterAccesses(startTime, showSystem) } - ?.sortedBy { -1 * it.discreteAccesses.first().accessTimeMs } ?.map { it.buildAppPermissionAccessUiInfo() } - ?: listOf() + ?.sortedBy { -it.accessStartTime } ?: listOf() } private fun LightHistoricalPackageOps.clusterAccesses( @@ -343,7 +349,9 @@ class PermissionUsageDetailsViewModel( ): List<AppPermissionDiscreteAccessCluster> { val clusters = mutableListOf<AppPermissionDiscreteAccessCluster>() val currentDiscreteAccesses = mutableListOf<DiscreteAccess>() - for (discreteAccess in appPermAccesses.discreteAccesses) { + // Iterate entries in asc order based on access timestamp. + for (index in appPermAccesses.discreteAccesses.size - 1 downTo 0) { + val discreteAccess = appPermAccesses.discreteAccesses[index] if (currentDiscreteAccesses.isEmpty()) { currentDiscreteAccesses.add(discreteAccess) } else if (!canAccessBeAddedToCluster(discreteAccess, currentDiscreteAccesses)) { @@ -352,7 +360,9 @@ class PermissionUsageDetailsViewModel( appPermAccesses.appPermissionId, appPermAccesses.attributionLabel, appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList() + currentDiscreteAccesses.toMutableList(), + if (isOpClusteredByItself(discreteAccess.opName)) discreteAccess.opName + else null ) ) currentDiscreteAccesses.clear() @@ -363,12 +373,14 @@ class PermissionUsageDetailsViewModel( } if (currentDiscreteAccesses.isNotEmpty()) { + val opName = currentDiscreteAccesses.last().opName clusters.add( AppPermissionDiscreteAccessCluster( appPermAccesses.appPermissionId, appPermAccesses.attributionLabel, appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList() + currentDiscreteAccesses.toMutableList(), + if (isOpClusteredByItself(opName)) opName else null ) ) } @@ -380,13 +392,37 @@ class PermissionUsageDetailsViewModel( * list that it can be added to the cluster. */ private fun canAccessBeAddedToCluster( - discreteAccess: DiscreteAccess, + currentAccess: DiscreteAccess, clusteredAccesses: List<DiscreteAccess> - ): Boolean = - discreteAccess.accessTimeMs / ONE_HOUR_MS == - clusteredAccesses.first().accessTimeMs / ONE_HOUR_MS && - clusteredAccesses.last().accessTimeMs / ONE_MINUTE_MS - - discreteAccess.accessTimeMs / ONE_MINUTE_MS <= CLUSTER_SPACING_MINUTES + ): Boolean { + val clusterOp = clusteredAccesses.last().opName + if ( + (isOpClusteredByItself(currentAccess.opName) || isOpClusteredByItself(clusterOp)) && + currentAccess.opName != clusteredAccesses.last().opName + ) { + return false + } + val currentAccessMinute = currentAccess.accessTimeMs / ONE_MINUTE_MS + val prevMostRecentAccessMillis = + clusteredAccesses.maxOf { discreteAccess -> + if (discreteAccess.accessDurationMs > 0) + discreteAccess.accessTimeMs + discreteAccess.accessDurationMs - ONE_MINUTE_MS + else discreteAccess.accessTimeMs + } + val prevMostRecentAccessMinute = prevMostRecentAccessMillis / ONE_MINUTE_MS + return (currentAccessMinute - prevMostRecentAccessMinute) <= CLUSTER_SPACING_MINUTES + } + + /** + * Determine if an op should be in its own cluster and hence display as an individual entry in + * the privacy timeline + */ + private fun isOpClusteredByItself(opName: String): Boolean { + if (isLocationByPassEnabled()) { + return opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION + } + return false + } /** * Composes all UI information from a [AppPermissionDiscreteAccessCluster] into a @@ -395,25 +431,42 @@ class PermissionUsageDetailsViewModel( private fun AppPermissionDiscreteAccessCluster.buildAppPermissionAccessUiInfo(): AppPermissionAccessUiInfo { val context = application - val accessTimeList = this.discreteAccesses.map { it.accessTimeMs } - val durationSummaryLabel = getDurationSummary(context, this, accessTimeList) + // The end minute is exclusive here in terms of access, i.e. [1..5) as the private data + // was not accessed at minute 5, it helps calculate the duration correctly. + val accessEndTimeMillis = + discreteAccesses.maxOf { appOpEvent -> + if (appOpEvent.accessDurationMs > 0) + appOpEvent.accessTimeMs + appOpEvent.accessDurationMs + else appOpEvent.accessTimeMs + ONE_MINUTE_MS + } + val accessStartTimeMillis = discreteAccesses.minOf { it.accessTimeMs } + val durationMs = accessEndTimeMillis - accessStartTimeMillis + val durationSummaryLabel = + if (durationMs >= TimeUnit.MINUTES.toMillis(CLUSTER_SPACING_MINUTES + 1)) { + getDurationUsedStr(context, durationMs) + } else null + val proxyLabel = getProxyPackageLabel(this) val subAttributionLabel = getSubAttributionLabel(this) - val showingSubAttribution = subAttributionLabel != null && subAttributionLabel.isNotEmpty() + val showingSubAttribution = !subAttributionLabel.isNullOrEmpty() val summary = buildUsageSummary(context, subAttributionLabel, proxyLabel, durationSummaryLabel) + val isEmergencyLocationAccess = + isLocationByPassEnabled() && clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION return AppPermissionAccessUiInfo( this.appPermissionId.userHandle, this.appPermissionId.packageName, getPackageLabel(this.appPermissionId.packageName, this.appPermissionId.userHandle), permissionGroup, - this.discreteAccesses.last().accessTimeMs, - this.discreteAccesses.first().accessTimeMs, + accessStartTimeMillis, + // Make the end time inclusive i.e. [1..4] + accessEndTimeMillis - ONE_MINUTE_MS, summary, showingSubAttribution, - ArrayList(this.attributionTags), - getBadgedPackageIcon(this.appPermissionId.packageName, this.appPermissionId.userHandle) + this.attributionTags.toSet(), + getBadgedPackageIcon(this.appPermissionId.packageName, this.appPermissionId.userHandle), + isEmergencyLocationAccess ) } @@ -455,36 +508,6 @@ class PermissionUsageDetailsViewModel( SubattributionUtils.isSubattributionSupported(lightPackageInfo) } - /** Returns a summary of the duration the permission was accessed for. */ - private fun getDurationSummary( - context: Context, - accessCluster: AppPermissionDiscreteAccessCluster, - accessTimeList: List<Long>, - ): String? { - if (accessTimeList.isEmpty()) { - return null - } - // Since Location accesses are atomic, we manually calculate the access duration by - // comparing the first and last access within the cluster. - val durationMs: Long = - if (permissionGroup == Manifest.permission_group.LOCATION) { - accessTimeList[0] - accessTimeList[accessTimeList.size - 1] - } else { - accessCluster.discreteAccesses - .filter { it.accessDurationMs > 0 } - .sumOf { it.accessDurationMs } - } - - // 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(accessCluster: AppPermissionDiscreteAccessCluster): String? = accessCluster.discreteAccesses @@ -497,12 +520,24 @@ class PermissionUsageDetailsViewModel( } /** Returns the attribution label for the permission access, if any. */ - private fun getSubAttributionLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? = - if (accessCluster.attributionLabel == Resources.ID_NULL) null + private fun getSubAttributionLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? { + // Special case for EMERGENCY_LOCATION app op. Show enforced attribution label in the + // Privacy Dashboard + if ( + isLocationByPassEnabled() && + accessCluster.clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION + ) { + return application.getString( + R.string.privacy_dashboard_emergency_location_enforced_attribution_label + ) + } + + return if (accessCluster.attributionLabel == Resources.ID_NULL) null else { val lightPackageInfo = getLightPackageInfo(accessCluster.appPermissionId) getSubAttributionLabels(lightPackageInfo)?.get(accessCluster.attributionLabel) } + } private fun getSubAttributionLabels(lightPackageInfo: LightPackageInfo?): Map<Int, String>? = if (lightPackageInfo == null) null @@ -528,34 +563,21 @@ class PermissionUsageDetailsViewModel( val accessEndTime: Long, val summaryText: CharSequence?, val showingAttribution: Boolean, - val attributionTags: ArrayList<String>, + val attributionTags: Set<String>, val badgedPackageIcon: Drawable?, + val isEmergencyLocationAccess: Boolean ) - /** - * Class containing all the information needed by the permission usage details fragments to - * render UI. - */ - data class PermissionUsageDetailsUiInfo( - /** - * Whether to show data over the last 7 days. - * - * While this information is available from the [SHOULD_SHOW_7_DAYS_KEY] state, we include - * it in the UI info so that it triggers a UI update when changed. - */ - private val show7Days: Boolean, - /** - * Whether to show system apps' data. - * - * While this information is available from the [SHOULD_SHOW_SYSTEM_KEY] state, we include - * it in the UI info so that it triggers a UI update when changed. - */ - private val showSystem: Boolean, - /** List of [AppPermissionAccessUiInfo]s to be displayed in the UI. */ - val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>, - /** Whether to show the "show/hide system" toggle. */ - val containsSystemAppAccesses: Boolean, - ) + sealed class PermissionUsageDetailsUiState { + data object Loading : PermissionUsageDetailsUiState() + + data class Success( + val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>, + val containsSystemAppUsage: Boolean, + val showSystem: Boolean, + val show7Days: Boolean, + ) : PermissionUsageDetailsUiState() + } /** * Data class representing a cluster of permission accesses close enough together to be @@ -566,6 +588,7 @@ class PermissionUsageDetailsViewModel( val attributionLabel: Int, val attributionTags: List<String>, val discreteAccesses: List<DiscreteAccess>, + val clusteredOp: String? ) /** @@ -579,9 +602,10 @@ class PermissionUsageDetailsViewModel( val discreteAccesses: List<DiscreteAccess> ) - /** [LiveData] object for [PermissionUsageDetailsUiInfo]. */ - val permissionUsagesDetailsInfoUiLiveData = - object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsageDetailsUiInfo>() { + /** [LiveData] object for [PermissionUsageDetailsUiState]. */ + private val _permissionUsagesDetailsInfoUiLiveData = + object : + SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsageDetailsUiState>() { private val getAppPermGroupUiInfoLiveData = { appPermissionId: AppPermissionId -> AppPermGroupUiInfoLiveData[ Triple( @@ -615,8 +639,7 @@ class PermissionUsageDetailsViewModel( ?.get(packageWithUserHandle) ?.appPermissionDiscreteAccesses ?.map { it.appPermissionId } - ?.toSet() - ?: setOf() + ?.toSet() ?: setOf() appPermissionIds.addAll(appPermGroupIds) } @@ -648,44 +671,21 @@ class PermissionUsageDetailsViewModel( } } - /** - * Returns the icon for the provided package name and user, by first searching the cache - * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo]. - */ - private fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? { - val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle) - if (packageIconCache.containsKey(packageNameWithUser)) { - return requireNotNull(packageIconCache[packageNameWithUser]) - } - val packageIcon = KotlinUtils.getBadgedPackageIcon(application, packageName, userHandle) - if (packageIcon != null) packageIconCache[packageNameWithUser] = packageIcon - - return packageIcon - } - - /** - * Returns the label for the provided package name, by first searching the cache otherwise - * retrieving it from the app's [android.content.pm.ApplicationInfo]. - */ - private fun getPackageLabel(packageName: String, user: UserHandle): String { - if (packageLabelCache.containsKey(packageName)) { - return requireNotNull(packageLabelCache[packageName]) - } + override fun getPermissionUsagesDetailsInfoUiLiveData(): + LiveData<PermissionUsageDetailsUiState> = _permissionUsagesDetailsInfoUiLiveData - val packageLabel = KotlinUtils.getPackageLabel(application, packageName, user) - packageLabelCache[packageName] = packageLabel + override fun getShowSystem(): Boolean = showSystemLiveData.value ?: false - return packageLabel - } + override fun getShow7Days(): Boolean = show7DaysLiveData.value ?: false /** Companion object for [PermissionUsageDetailsViewModel]. */ companion object { - private const val ONE_HOUR_MS = 3_600_000 - private const val ONE_MINUTE_MS = 60_000 - private const val CLUSTER_SPACING_MINUTES: Long = 1L + const val ONE_HOUR_MS = 3_600_000 + const val ONE_MINUTE_MS = 60_000 + const val CLUSTER_SPACING_MINUTES: Long = 1L private const val TELECOM_PACKAGE = "com.android.server.telecom" - private val TIME_7_DAYS_DURATION: Long = DAYS.toMillis(7) - private val TIME_24_HOURS_DURATION: Long = DAYS.toMillis(1) + val TIME_7_DAYS_DURATION: Long = DAYS.toMillis(7) + val TIME_24_HOURS_DURATION: Long = DAYS.toMillis(1) internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem" internal const val SHOULD_SHOW_7_DAYS_KEY = "show7Days" @@ -705,6 +705,9 @@ class PermissionUsageDetailsViewModel( if (SdkLevel.isAtLeastT()) { add(AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO) } + if (isLocationByPassEnabled()) { + add(AppOpsManager.OPSTR_EMERGENCY_LOCATION) + } } /** Creates the [Intent] for the click action of a privacy dashboard app usage event. */ @@ -716,7 +719,7 @@ class PermissionUsageDetailsViewModel( accessStartTime: Long, accessEndTime: Long, showingAttribution: Boolean, - attributionTags: List<String> + attributionTags: Set<String> ): Intent { return getManagePermissionUsageIntent( context, @@ -726,8 +729,7 @@ class PermissionUsageDetailsViewModel( accessEndTime, showingAttribution, attributionTags - ) - ?: getDefaultManageAppPermissionsIntent(packageName, userHandle) + ) ?: getDefaultManageAppPermissionsIntent(packageName, userHandle) } /** @@ -741,7 +743,7 @@ class PermissionUsageDetailsViewModel( accessStartTime: Long, accessEndTime: Long, showingAttribution: Boolean, - attributionTags: List<String> + attributionTags: Set<String> ): Intent? { if ( !showingAttribution || @@ -790,6 +792,9 @@ class PermissionUsageDetailsViewModel( putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) } } + + private fun isLocationByPassEnabled(): Boolean = + SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled() } /** Factory for [PermissionUsageDetailsViewModel]. */ @@ -805,7 +810,14 @@ class PermissionUsageDetailsViewModel( handle: SavedStateHandle, ): T { @Suppress("UNCHECKED_CAST") - return PermissionUsageDetailsViewModel(app, handle, permissionGroup) as T + return if ( + com.android.permission.flags.Flags.livedataRefactorPermissionTimelineEnabled() + ) { + Log.d("PermissionTimeline", "timeline refactor flag enabled..") + PermissionUsageDetailsViewModelV2.create(app, handle, permissionGroup) as T + } else { + PermissionUsageDetailsViewModel(app, handle, permissionGroup) as T + } } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt new file mode 100644 index 000000000..312a7ee20 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelV2.kt @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.model.v31 + +import android.app.AppOpsManager.OPSTR_EMERGENCY_LOCATION +import android.app.Application +import android.graphics.drawable.Drawable +import android.os.UserHandle +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LiveData +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.R +import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository +import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper +import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase +import com.android.permissioncontroller.permission.domain.usecase.v31.isLocationByPassEnabled +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_7_DAYS_KEY +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.TIME_24_HOURS_DURATION +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.TIME_7_DAYS_DURATION +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.role.data.repository.v31.RoleRepository +import com.android.permissioncontroller.user.data.repository.v31.UserRepository +import java.time.Instant +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn + +class PermissionUsageDetailsViewModelV2( + app: Application, + private val getPermissionUsageDetailsUseCase: GetPermissionGroupUsageDetailsUseCase, + private val state: SavedStateHandle, + private val permissionGroup: String, + scope: CoroutineScope? = null, + private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, + private val packageRepository: PackageRepository = PackageRepository.getInstance(app) +) : BasePermissionUsageDetailsViewModel(app) { + private val coroutineScope = scope ?: viewModelScope + private val context = app + + private val showSystemFlow = MutableStateFlow(state[SHOULD_SHOW_SYSTEM_KEY] ?: false) + private val show7DaysFlow = MutableStateFlow(state[SHOULD_SHOW_7_DAYS_KEY] ?: false) + + private val permissionTimelineUsagesFlow: + StateFlow<PermissionTimelineUsageModelWrapper> by lazy { + getPermissionUsageDetailsUseCase(coroutineScope) + .flowOn(defaultDispatcher) + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(5000), + PermissionTimelineUsageModelWrapper.Loading + ) + } + + @VisibleForTesting + val permissionUsageDetailsUiStateFlow: Flow<PermissionUsageDetailsUiState> by lazy { + combine(permissionTimelineUsagesFlow, showSystemFlow, show7DaysFlow) { + permissionTimelineUsages, + showSystem, + show7Days -> + permissionTimelineUsages.buildPermissionUsageDetailsUiInfo(showSystem, show7Days) + } + .flowOn(defaultDispatcher) + } + + override fun getPermissionUsagesDetailsInfoUiLiveData(): + LiveData<PermissionUsageDetailsUiState> { + return permissionUsageDetailsUiStateFlow.asLiveData( + context = coroutineScope.coroutineContext + ) + } + + private fun PermissionTimelineUsageModelWrapper.buildPermissionUsageDetailsUiInfo( + showSystem: Boolean, + show7Days: Boolean + ): PermissionUsageDetailsUiState { + if (this is PermissionTimelineUsageModelWrapper.Loading) { + return PermissionUsageDetailsUiState.Loading + } + val timelineUsageModels = + (this as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels + val startTime = + (System.currentTimeMillis() - getUsageDuration(show7Days)).coerceAtLeast( + Instant.EPOCH.toEpochMilli() + ) + + val permissionTimelineUsageModels = + timelineUsageModels.filter { it.accessEndMillis > startTime } + val containsSystemUsages = permissionTimelineUsageModels.any { !it.isUserSensitive } + val result = + permissionTimelineUsageModels + .filter { showSystem || it.isUserSensitive } + .map { clusterOps -> + val durationSummaryLabel = + if (clusterOps.durationMillis > 0) { + getDurationSummary(clusterOps.durationMillis) + } else { + null + } + val proxyLabel = getProxyPackageLabel(clusterOps) + val isEmergencyLocationAccess = + isLocationByPassEnabled() && + clusterOps.opNames.any { it == OPSTR_EMERGENCY_LOCATION } + val subAttributionLabel = + if (isEmergencyLocationAccess) { + emergencyLocationAttributionLabel + } else { + clusterOps.attributionLabel + } + val showingSubAttribution = !subAttributionLabel.isNullOrEmpty() + val summary = + buildUsageSummary(subAttributionLabel, proxyLabel, durationSummaryLabel) + PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo( + UserHandle.of(clusterOps.userId), + clusterOps.packageName, + getPackageLabel(clusterOps.packageName, UserHandle.of(clusterOps.userId)), + permissionGroup, + clusterOps.accessStartMillis, + clusterOps.accessEndMillis, + summary, + showingSubAttribution, + clusterOps.attributionTags ?: emptySet(), + getBadgedPackageIcon( + clusterOps.packageName, + UserHandle.of(clusterOps.userId) + ), + isEmergencyLocationAccess + ) + } + .sortedBy { -1 * it.accessStartTime } + return PermissionUsageDetailsUiState.Success( + result, + containsSystemUsages, + showSystem, + show7Days + ) + } + + private val emergencyLocationAttributionLabel: String by lazy { + context.getString(R.string.privacy_dashboard_emergency_location_enforced_attribution_label) + } + + override fun getShowSystem(): Boolean = showSystemFlow.value + + override val showSystemLiveData = + showSystemFlow.asLiveData(context = coroutineScope.coroutineContext) + + override fun getShow7Days(): Boolean = show7DaysFlow.value + + private fun getUsageDuration(show7Days: Boolean): Long { + return if (show7Days && DeviceUtils.isHandheld()) { + TIME_7_DAYS_DURATION + } else { + TIME_24_HOURS_DURATION + } + } + + private fun getProxyPackageLabel(accessCluster: PermissionTimelineUsageModel): String? = + accessCluster.proxyPackageName?.let { proxyPackageName -> + if (accessCluster.proxyUserId != null) { + getPackageLabel(proxyPackageName, UserHandle.of(accessCluster.proxyUserId)) + } else null + } + + override fun updateShowSystemAppsToggle(showSystem: Boolean) { + if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) { + state[SHOULD_SHOW_SYSTEM_KEY] = showSystem + } + showSystemFlow.compareAndSet(!showSystem, showSystem) + } + + override fun updateShow7DaysToggle(show7Days: Boolean) { + if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) { + state[SHOULD_SHOW_7_DAYS_KEY] = show7Days + } + show7DaysFlow.compareAndSet(!show7Days, show7Days) + } + + // TODO review these methods when old impl is removed, suspend function?? + override fun getPackageLabel(app: Application, packageName: String, user: UserHandle): String { + return packageRepository.getPackageLabel(packageName, user) + } + + override fun getBadgedPackageIcon( + app: Application, + packageName: String, + user: UserHandle + ): Drawable? { + return packageRepository.getBadgedPackageIcon(packageName, user) + } + + companion object { + fun create( + app: Application, + handle: SavedStateHandle, + permissionGroup: String + ): PermissionUsageDetailsViewModelV2 { + val permissionRepository = PermissionRepository.getInstance(app) + val packageRepository = PackageRepository.getInstance(app) + val appOpRepository = AppOpRepository.getInstance(app, permissionRepository) + val roleRepository = RoleRepository.getInstance(app) + val userRepository = UserRepository.getInstance(app) + val useCase = + GetPermissionGroupUsageDetailsUseCase( + permissionGroup, + packageRepository, + permissionRepository, + appOpRepository, + roleRepository, + userRepository + ) + return PermissionUsageDetailsViewModelV2(app, useCase, handle, permissionGroup) + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java index 43cad8335..3fbefc742 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java @@ -250,7 +250,7 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { // TODO: No hardcoded layouts SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext()); - pref.setLayoutResource(R.layout.preference_permissions); + pref.setLayoutResource(R.layout.preference_permissions_television); pref.setChecked(permGroup.areRuntimePermissionsGranted(filterPermissions)); pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group)); pref.setTitle(perm.loadLabel(getActivity().getPackageManager())); @@ -277,7 +277,7 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { // TODO: No hardcoded layouts Preference pref = new Preference(getActivity()); - pref.setLayoutResource(R.layout.preference_permissions); + pref.setLayoutResource(R.layout.preference_permissions_television); pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group)); pref.setTitle(perm.loadLabel(pm)); pref.setPersistent(false); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java index 399a694d0..eb2f95a24 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java @@ -44,6 +44,7 @@ import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -55,6 +56,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.fragment.app.DialogFragment; import androidx.lifecycle.ViewModelProvider; @@ -499,6 +501,7 @@ public class AppPermissionFragment extends SettingsWithHeader } @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void showAdvancedConfirmDialog(AdvancedConfirmDialogArgs args) { AlertDialog.Builder b = new AlertDialog.Builder(getContext()) .setIcon(args.getIconId()) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TEST_MAPPING new file mode 100644 index 000000000..1a74fb208 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TEST_MAPPING @@ -0,0 +1,19 @@ +{ + "tv-postsubmit": [ + { + "name": "PermissionUiTestCases", + "options": [ + {"include-filter": "com.android.permissioncontroller.permissionui.ui.television"}, + {"exclude-annotation": "org.junit.Ignore"}, + {"exclude-annotation": "androidx.test.filters.FlakyTest"} + ] + }, + { + "name": "CtsPermissionUiTestCases", + "options": [ + {"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"}, + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ] +}
\ No newline at end of file diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt index 2741a3cd8..e763afc30 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt @@ -19,32 +19,35 @@ package com.android.permissioncontroller.permission.ui.viewmodel.v31 import android.app.Application import android.content.Context import android.os.Build -import android.os.Bundle import androidx.annotation.RequiresApi import androidx.annotation.VisibleForTesting -import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.asLiveData +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope -import androidx.savedstate.SavedStateRegistryOwner +import androidx.lifecycle.viewmodel.CreationExtras +import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModelWrapper import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageUseCase -import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_7_DAYS_KEY +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY import java.time.Instant import java.util.concurrent.TimeUnit -import kotlin.concurrent.Volatile import kotlin.math.max import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.runBlocking @@ -55,43 +58,41 @@ class PermissionUsageViewModel( private val getPermissionUsageUseCase: GetPermissionGroupUsageUseCase, scope: CoroutineScope? = null, private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default, - // Inject the parameter to prevent READ_DEVICE_CONFIG permission error on T- platforms. - private val is7DayToggleEnabled: Boolean = KotlinUtils.is7DayToggleEnabled(), + private val savedState: SavedStateHandle = SavedStateHandle(emptyMap()), ) : AndroidViewModel(app) { - private var showSystemApps = false - private var show7DaysData = false + private val showSystemFlow = MutableStateFlow(savedState[SHOULD_SHOW_SYSTEM_KEY] ?: false) + private val show7DaysFlow = MutableStateFlow(savedState[SHOULD_SHOW_7_DAYS_KEY] ?: false) private val coroutineScope = scope ?: viewModelScope - // Cache permission usages to calculate ui state for "show system" and "show 7 days" toggle. - @Volatile private var permissionGroupUsages = emptyList<PermissionGroupUsageModel>() - - private val permissionUsagesUiStateFlow: StateFlow<PermissionUsagesUiState> by lazy { + private val permissionUsagesUiStateFlow: StateFlow<PermissionGroupUsageModelWrapper> by lazy { getPermissionUsageUseCase() - .map { permGroupUsages -> - permissionGroupUsages = permGroupUsages - buildPermissionUsagesUiState(permGroupUsages) - } .flowOn(defaultDispatcher) .stateIn( coroutineScope, SharingStarted.WhileSubscribed(5000), - PermissionUsagesUiState.Loading + PermissionGroupUsageModelWrapper.Loading ) } - val permissionUsagesUiLiveData = - permissionUsagesUiStateFlow.asLiveData(context = coroutineScope.coroutineContext) - @VisibleForTesting - fun getPermissionUsagesUiDataFlow(): Flow<PermissionUsagesUiState> { - return permissionUsagesUiStateFlow + val permissionUsagesUiDataFlow: Flow<PermissionUsagesUiState> by lazy { + combine(permissionUsagesUiStateFlow, showSystemFlow, show7DaysFlow) { + permGroupUsages, + showSystemApps, + show7Days -> + buildPermissionUsagesUiState(permGroupUsages, showSystemApps, show7Days) + } + .flowOn(defaultDispatcher) } + val permissionUsagesUiLiveData = + permissionUsagesUiDataFlow.asLiveData(context = coroutineScope.coroutineContext) + /** Get start time based on whether to show 24 hours or 7 days data. */ private fun getStartTime(show7DaysData: Boolean): Long { val curTime = System.currentTimeMillis() val showPermissionUsagesDuration = - if (is7DayToggleEnabled && show7DaysData) { + if (show7DaysData && DeviceUtils.isHandheld()) { TIME_7_DAYS_DURATION } else { TIME_24_HOURS_DURATION @@ -101,8 +102,17 @@ class PermissionUsageViewModel( /** Builds a [PermissionUsagesUiState] containing all data necessary to render the UI. */ private fun buildPermissionUsagesUiState( - permissionGroupOps: List<PermissionGroupUsageModel> + usages: PermissionGroupUsageModelWrapper, + showSystemApps: Boolean, + show7DaysData: Boolean, ): PermissionUsagesUiState { + if (usages is PermissionGroupUsageModelWrapper.Loading) { + return PermissionUsagesUiState.Loading + } + + val permissionGroupOps: List<PermissionGroupUsageModel> = + (usages as PermissionGroupUsageModelWrapper.Success).permissionUsageModels + val startTime = getStartTime(show7DaysData) val dashboardPermissionGroups = permissionRepository.getPermissionGroupsForPrivacyDashboard() @@ -120,26 +130,31 @@ class PermissionUsageViewModel( } return PermissionUsagesUiState.Success( permGroupOps.any { !it.isUserSensitive }, - permissionUsageCountMap + permissionUsageCountMap, + showSystemApps, + show7DaysData ) } - fun getShowSystemApps(): Boolean { - return showSystemApps - } + fun getShowSystemApps(): Boolean = showSystemFlow.value - fun getShow7DaysData(): Boolean { - return show7DaysData - } + fun getShow7DaysData(): Boolean = show7DaysFlow.value + + val showSystemAppsLiveData = + showSystemFlow.asLiveData(context = coroutineScope.coroutineContext) - fun updateShowSystem(showSystem: Boolean): PermissionUsagesUiState { - showSystemApps = showSystem - return buildPermissionUsagesUiState(permissionGroupUsages) + fun updateShowSystem(showSystem: Boolean) { + if (showSystem != savedState[SHOULD_SHOW_SYSTEM_KEY]) { + savedState[SHOULD_SHOW_SYSTEM_KEY] = showSystem + } + showSystemFlow.compareAndSet(!showSystem, showSystem) } - fun updateShow7Days(show7Days: Boolean): PermissionUsagesUiState { - show7DaysData = show7Days - return buildPermissionUsagesUiState(permissionGroupUsages) + fun updateShow7Days(show7Days: Boolean) { + if (show7Days != savedState[SHOULD_SHOW_7_DAYS_KEY]) { + savedState[SHOULD_SHOW_7_DAYS_KEY] = show7Days + } + show7DaysFlow.compareAndSet(!show7Days, show7Days) } private val permissionGroupLabels = mutableMapOf<String, String>() @@ -163,27 +178,28 @@ class PermissionUsageViewModel( /** Data class to hold all the information required to configure the UI. */ sealed class PermissionUsagesUiState { data object Loading : PermissionUsagesUiState() + data class Success( - val shouldShowSystemToggle: Boolean, - val permissionGroupUsageCount: Map<String, Int> + val containsSystemAppUsage: Boolean, + val permissionGroupUsageCount: Map<String, Int>, + val showSystem: Boolean, + val show7Days: Boolean, ) : PermissionUsagesUiState() } /** Factory for [PermissionUsageViewModel]. */ @RequiresApi(Build.VERSION_CODES.S) -class PermissionUsageViewModelFactory( - private val app: Application, - owner: SavedStateRegistryOwner, - defaultArgs: Bundle -) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { +class PermissionUsageViewModelFactory(private val app: Application) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create( - key: String, - modelClass: Class<T>, - handle: SavedStateHandle - ): T { + override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { val permissionRepository = PermissionRepository.getInstance(app) val permissionUsageUseCase = GetPermissionGroupUsageUseCase.create(app) - return PermissionUsageViewModel(app, permissionRepository, permissionUsageUseCase) as T + return PermissionUsageViewModel( + app, + permissionRepository, + permissionUsageUseCase, + savedState = extras.createSavedStateHandle() + ) + as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/package-info.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/package-info.java new file mode 100644 index 000000000..ab144afa8 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.S) +package com.android.permissioncontroller.permission.ui.viewmodel.v31; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt new file mode 100644 index 000000000..510d19706 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.wear + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.wear.compose.foundation.SwipeToDismissValue +import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState +import androidx.wear.compose.material.ChipDefaults +import androidx.wear.compose.material.MaterialTheme +import androidx.wear.compose.material.SwipeToDismissBox +import com.android.permissioncontroller.permission.ui.wear.elements.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold +import com.android.permissioncontroller.permission.ui.wear.model.LocationProviderInterceptDialogArgs +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion + +@Composable +fun LocationProviderDialogScreen(args: LocationProviderInterceptDialogArgs?) { + args?.apply { + val state = rememberSwipeToDismissBoxState() + LaunchedEffect(state.currentValue) { + // If the swipe is complete + if (state.currentValue == SwipeToDismissValue.Dismissed) { + onOkButtonClick() + } + } + SwipeToDismissBox(state = state) { isBackground -> + WearPermissionScaffold( + materialUIVersion = WearPermissionMaterialUIVersion.MATERIAL2_5, + showTimeText = false, + image = iconId, + title = stringResource(titleId), + subtitle = message, + isLoading = isBackground, + content = { + item { + Chip( + label = stringResource(locationSettingsId), + onClick = onLocationSettingsClick, + modifier = Modifier.fillMaxWidth(), + textColor = MaterialTheme.colors.surface, + colors = ChipDefaults.primaryChipColors(), + ) + } + item { + Chip( + label = stringResource(okButtonTitleId), + onClick = onOkButtonClick, + modifier = Modifier.fillMaxWidth(), + ) + } + }, + ) + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt index 5e6929cfb..959dc9137 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt @@ -19,11 +19,13 @@ package com.android.permissioncontroller.permission.ui.wear import android.Manifest import android.app.Activity import android.content.Intent +import android.os.Build import android.os.Bundle import android.os.UserHandle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.compose.ui.platform.ComposeView import androidx.core.os.BundleCompat @@ -271,6 +273,7 @@ class WearAppPermissionFragment : Fragment(), ConfirmDialogShowingFragment { confirmDialogViewModel.showConfirmDialogLiveData.value = true } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun showAdvancedConfirmDialog(args: AdvancedConfirmDialogArgs) { confirmDialogViewModel.advancedConfirmDialogArgs = args confirmDialogViewModel.showAdvancedConfirmDialogLiveData.value = true diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt index 9b960dfb5..552876b7d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt @@ -46,8 +46,9 @@ import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGr import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModelFactory import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModelFactory import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme -import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled import java.time.Instant import java.util.concurrent.TimeUnit @@ -68,17 +69,15 @@ class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallb val user = arguments?.let { BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)!! - } - ?: UserHandle.SYSTEM + } ?: UserHandle.SYSTEM val activity: Activity = requireActivity() val packageManager = activity.packageManager - val packageInfo: PackageInfo? = try { packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) } catch (e: PackageManager.NameNotFoundException) { - Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e) + Log.i(LOG_TAG, "No package:" + activity.callingPackage, e) null } @@ -88,16 +87,30 @@ class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallb return null } val sessionId = arguments?.getLong(EXTRA_SESSION_ID, 0) ?: 0 - val appPermissions = AppPermissions(activity, packageInfo, true, { activity.finish() }) - val factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId) + + val appPermissions = AppPermissions(activity, packageInfo, true) { activity.finish() } + val viewModel = - ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel::class.java) + ViewModelProvider( + owner = this, + factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId) + )[AppPermissionGroupsViewModel::class.java] + wearViewModel = - ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) - .get(WearAppPermissionUsagesViewModel::class.java) + ViewModelProvider(owner = this, factory = WearAppPermissionUsagesViewModelFactory())[ + WearAppPermissionUsagesViewModel::class.java] + val revokeDialogViewModel = - ViewModelProvider(this, AppPermissionGroupsRevokeDialogViewModelFactory()) - .get(AppPermissionGroupsRevokeDialogViewModel::class.java) + ViewModelProvider( + owner = this, + factory = AppPermissionGroupsRevokeDialogViewModelFactory() + )[AppPermissionGroupsRevokeDialogViewModel::class.java] + + val locationProviderInterceptDialogViewModel = + ViewModelProvider( + owner = this, + factory = WearLocationProviderInterceptDialogViewModelFactory() + )[WearLocationProviderInterceptDialogViewModel::class.java] val context = requireContext() @@ -106,13 +119,10 @@ class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallb if (SdkLevel.isAtLeastS()) { permissionUsages = PermissionUsages(context) val aggregateDataFilterBeginDays = - (if (is7DayToggleEnabled()) - AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 - else AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1) - .toLong() + AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1.toLong() val filterTimeBeginMillis = - Math.max( + maxOf( System.currentTimeMillis() - TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays), Instant.EPOCH.toEpochMilli() @@ -123,7 +133,7 @@ class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallb filterTimeBeginMillis, Long.MAX_VALUE, PermissionUsages.USAGE_FLAG_LAST, - requireActivity().getLoaderManager(), + requireActivity().loaderManager, false, false, this, @@ -140,7 +150,8 @@ class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallb appPermissions = appPermissions, viewModel = viewModel, wearViewModel = wearViewModel, - revokeDialogViewModel = revokeDialogViewModel + revokeDialogViewModel = revokeDialogViewModel, + locationProviderInterceptDialogViewModel = locationProviderInterceptDialogViewModel ) return ComposeView(activity).apply { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt index 9529ac83a..2933d6fda 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt @@ -17,9 +17,9 @@ package com.android.permissioncontroller.permission.ui.wear import android.content.Context -import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PermissionInfo +import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Build import android.os.UserHandle import android.util.ArraySet @@ -35,14 +35,13 @@ import com.android.permissioncontroller.permission.model.Permission import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState 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.AppPermissionFragment import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.PermSubtitle import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModel import com.android.permissioncontroller.permission.utils.ArrayUtils import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.Utils @@ -59,6 +58,7 @@ class WearAppPermissionGroupsHelper( val viewModel: AppPermissionGroupsViewModel, val wearViewModel: WearAppPermissionUsagesViewModel, val revokeDialogViewModel: AppPermissionGroupsRevokeDialogViewModel, + val locationProviderInterceptDialogViewModel: WearLocationProviderInterceptDialogViewModel, private val toggledGroups: ArraySet<AppPermissionGroup> = ArraySet() ) { fun getPermissionGroupChipParams( @@ -310,9 +310,7 @@ class WearAppPermissionGroupsHelper( addToggledGroup(group) if (LocationUtils.isLocationGroupAndProvider(context, permGroupName, packageName)) { - val intent = Intent(context, LocationProviderInterceptDialog::class.java) - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) - context.startActivityAsUser(intent, user) + locationProviderInterceptDialogViewModel.showDialog(context, packageName) } else if ( LocationUtils.isLocationGroupAndControllerExtraPackage( context, @@ -322,9 +320,13 @@ class WearAppPermissionGroupsHelper( ) { // Redirect to location controller extra package settings. LocationUtils.startLocationControllerExtraPackageSettings(context, user) + } else if (permGroupName.equals(HEALTH_PERMISSION_GROUP) + && android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) { + // Redirect to Health&Fitness UI + Utils.navigateToAppHealthConnectSettings(fragment.requireContext(), packageName, user) } else { val args = - AppPermissionFragment.createArgs( + WearAppPermissionFragment.createArgs( packageName, null, permGroupName, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt index 0883666fc..ba37205a6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt @@ -39,6 +39,11 @@ fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) { val autoRevoke = helper.viewModel.autoRevokeLiveData.observeAsState(null) val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList()) val showRevokeDialog = helper.revokeDialogViewModel.showDialogLiveData.observeAsState(false) + val showLocationProviderDialog = + helper.locationProviderInterceptDialogViewModel.dialogVisibilityLiveData.observeAsState( + false + ) + var isLoading by remember { mutableStateOf(true) } Box { @@ -51,6 +56,11 @@ fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) { showDialog = showRevokeDialog.value, args = helper.revokeDialogViewModel.revokeDialogArgs ) + if (showLocationProviderDialog.value) { + LocationProviderDialogScreen( + helper.locationProviderInterceptDialogViewModel.locationProviderInterceptDialogArgs + ) + } } if (isLoading && !packagePermGroups.value.isNullOrEmpty()) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationDialogFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationDialogFragment.kt index c978f5968..e81fd948d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationDialogFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationDialogFragment.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.wear import android.content.Context +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -35,6 +36,7 @@ class WearEnhancedConfirmationDialogFragment : DialogFragment() { override fun onAttach(context: Context) { super.onAttach(context) enhancedConfirmationDialogActivity = context as EnhancedConfirmationDialogActivity + enhancedConfirmationDialogActivity.window.decorView.setBackgroundColor(Color.TRANSPARENT) } override fun onCreateView( diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt index 349ec1dfe..1c31ec96f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.wear import android.app.Activity +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -131,7 +132,7 @@ fun WearEnhancedConfirmationScreen( SwipeToDismissBox(state = state) { isBackground -> if (isBackground || dismissed) { - Box(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } } else { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt index 950353f52..1498b91b6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt @@ -37,17 +37,20 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.N 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.wear.GrantPermissionsWearViewHandler.BUTTON_RES_ID_TO_NUM -import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel +import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3 @Composable fun WearGrantPermissionsScreen( viewModel: WearGrantPermissionsViewModel, onButtonClicked: (Int) -> Unit, - onLocationSwitchChanged: (Boolean) -> Unit + onLocationSwitchChanged: (Boolean) -> Unit, ) { val groupMessage = viewModel.groupMessageLiveData.observeAsState("") val icon = viewModel.iconLiveData.observeAsState(null) @@ -55,8 +58,15 @@ fun WearGrantPermissionsScreen( val locationVisibilities = viewModel.locationVisibilitiesLiveData.observeAsState(emptyList()) val preciseLocationChecked = viewModel.preciseLocationCheckedLiveData.observeAsState(false) val buttonVisibilities = viewModel.buttonVisibilitiesLiveData.observeAsState(emptyList()) + val materialUIVersion = + if (ResourceHelper.material3Enabled) { + MATERIAL3 + } else { + MATERIAL2_5 + } ScrollableScreen( + materialUIVersion = materialUIVersion, showTimeText = false, image = icon.value, title = groupMessage.value, @@ -69,13 +79,14 @@ fun WearGrantPermissionsScreen( locationVisibilities.value.getOrElse(DIALOG_WITH_BOTH_LOCATIONS) { false } ) { item { - ToggleChip( + WearPermissionToggleControl( checked = preciseLocationChecked.value, - onCheckedChanged = { onLocationSwitchChanged(it) }, + onCheckedChanged = onLocationSwitchChanged, label = stringResource(R.string.app_permission_location_accuracy), toggleControl = ToggleChipToggleControl.Switch, modifier = Modifier.fillMaxWidth(), - labelMaxLine = Integer.MAX_VALUE + labelMaxLines = Integer.MAX_VALUE, + materialUIVersion = materialUIVersion, ) } } @@ -87,16 +98,17 @@ fun WearGrantPermissionsScreen( } if (buttonVisibilities.value[pos]) { item { - Chip( + WearPermissionButton( label = getPrimaryText( - pos, - locationVisibilities.value, - labelsByButton(BUTTON_RES_ID_TO_NUM.valueAt(i)) + pos = pos, + locationVisibilities = locationVisibilities.value, + default = labelsByButton(BUTTON_RES_ID_TO_NUM.valueAt(i)), ), onClick = { onButtonClicked(BUTTON_RES_ID_TO_NUM.keyAt(i)) }, modifier = Modifier.fillMaxWidth(), - labelMaxLines = Integer.MAX_VALUE + labelMaxLines = Integer.MAX_VALUE, + materialUIVersion = materialUIVersion, ) } } @@ -108,7 +120,7 @@ fun setContent( composeView: ComposeView, viewModel: WearGrantPermissionsViewModel, onButtonClicked: (Int) -> Unit, - onLocationSwitchChanged: (Boolean) -> Unit + onLocationSwitchChanged: (Boolean) -> Unit, ) { composeView.setContent { WearGrantPermissionsScreen(viewModel, onButtonClicked, onLocationSwitchChanged) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt index bd1946759..9aacd65d3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.wear import android.graphics.drawable.Drawable +import android.permission.flags.Flags import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -77,7 +78,13 @@ internal fun getPermGroupChipParams( } return permissionGroups // Removing Health Connect from the list of permissions to fix b/331260850 - .filterNot { Utils.isHealthPermissionGroup(it.key) } + .let { + if (Flags.replaceBodySensorPermissionEnabled()) { + it + } else { + it.filterNot { Utils.isHealthPermissionGroup(it.key) } + } + } .mapNotNull { val uiInfo = it.value ?: return@mapNotNull null PermGroupChipParam( diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt index d8eb71e0e..783ca593b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt @@ -33,11 +33,12 @@ import com.android.permissioncontroller.Constants 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.handheld.AppPermissionFragment import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModelFactory /** * This is a condensed version of @@ -65,7 +66,7 @@ class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { val permGroupName = arguments?.getString(Intent.EXTRA_PERMISSION_GROUP_NAME) ?: arguments?.getString(Intent.EXTRA_PERMISSION_NAME) - ?: throw RuntimeException("Permission group name must not be null.") + ?: throw RuntimeException("Permission group name must not be null.") val sessionId: Long = arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID val isStorageAndLessThanT = @@ -79,13 +80,19 @@ class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) .get(WearAppPermissionUsagesViewModel::class.java) + val locationProviderDialogViewModel = + ViewModelProvider( + owner = this, + factory = WearLocationProviderInterceptDialogViewModelFactory() + )[WearLocationProviderInterceptDialogViewModel::class.java] + val onAppClick: (String, UserHandle, String) -> Unit = { packageName, user, category -> run { viewModel.navigateToAppPermission( this, packageName, user, - AppPermissionFragment.createArgs( + WearAppPermissionFragment.createArgs( packageName, null, permGroupName, @@ -114,7 +121,7 @@ class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { isAllowedForeground, isDenied, sessionId, - activity.getApplication(), + activity.application, permGroupName, LOG_TAG ) @@ -133,7 +140,7 @@ class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { filterTimeBeginMillis, Long.MAX_VALUE, PermissionUsages.USAGE_FLAG_LAST, - requireActivity().getLoaderManager(), + requireActivity().loaderManager, false, false, this, @@ -145,10 +152,12 @@ class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { setContent { WearPermissionAppsScreen( WearPermissionAppsHelper( - activity.getApplication(), + activity.application, + this@WearPermissionAppsFragment.requireContext(), permGroupName, viewModel, wearViewModel, + locationProviderDialogViewModel, isStorageAndLessThanT, onAppClick, onShowSystemClick, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt index d14cdb620..a5d24e983 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.wear import android.app.Application +import android.content.Context import android.graphics.drawable.Drawable import android.os.UserHandle import com.android.permission.flags.Flags @@ -25,9 +26,11 @@ import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModel import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupDescription import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel +import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.settingslib.utils.applications.AppUtils import java.text.Collator import java.util.Random @@ -35,20 +38,28 @@ import java.util.Random /** Helper class for WearPermissionsAppScreen. */ class WearPermissionAppsHelper( val application: Application, + val context: Context, val permGroupName: String, val viewModel: PermissionAppsViewModel, val wearViewModel: WearAppPermissionUsagesViewModel, + val locationProviderDialogViewModel: WearLocationProviderInterceptDialogViewModel, private val isStorageAndLessThanT: Boolean, private val onAppClick: (String, UserHandle, String) -> Unit, val onShowSystemClick: (Boolean) -> Unit, val logFragmentCreated: (String, UserHandle, Long, Boolean, Boolean, Boolean) -> Unit ) { fun categorizedAppsLiveData() = viewModel.categorizedAppsLiveData + fun hasSystemAppsLiveData() = viewModel.hasSystemAppsLiveData + fun shouldShowSystemLiveData() = viewModel.shouldShowSystemLiveData + fun showAlways() = viewModel.showAllowAlwaysStringLiveData.value ?: false + fun getTitle() = getPermGroupLabel(application, permGroupName).toString() + fun getSubTitle() = getPermGroupDescription(application, permGroupName).toString() + fun getChipsByCategory( categorizedApps: Map<Category, List<Pair<String, UserHandle>>>, appPermissionUsages: List<AppPermissionUsage> @@ -64,7 +75,7 @@ class WearPermissionAppsHelper( val comparator = ChipComparator(collator) val viewIdForLogging = Random().nextLong() - for (category in Category.values()) { + for (category in Category.entries) { if (category == Category.ALLOWED && isStorageAndLessThanT) { val allowedList = categorizedApps[Category.ALLOWED] if (!allowedList.isNullOrEmpty()) { @@ -169,9 +180,21 @@ class WearPermissionAppsHelper( title = KotlinUtils.getPackageLabel(application, packageName, user), summary = summary, contentDescription = - AppUtils.getAppContentDescription(application, packageName, user.getIdentifier()), + AppUtils.getAppContentDescription(application, packageName, user.identifier), icon = KotlinUtils.getBadgedPackageIcon(application, packageName, user), - onClick = { onClick(packageName, user, category.categoryName) } + onClick = { + if ( + LocationUtils.isLocationGroupAndProvider( + application.applicationContext, + permGroupName, + packageName + ) + ) { + locationProviderDialogViewModel.showDialog(context, packageName) + } else { + onClick(packageName, user, category.categoryName) + } + } ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt index d694f20f8..8e779cb8c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt @@ -16,6 +16,8 @@ package com.android.permissioncontroller.permission.ui.wear +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable @@ -40,6 +42,8 @@ fun WearPermissionAppsScreen(helper: WearPermissionAppsHelper) { val categorizedApps = helper.categorizedAppsLiveData().observeAsState(emptyMap()) val hasSystemApps = helper.hasSystemAppsLiveData().observeAsState(false) val showSystem = helper.shouldShowSystemLiveData().observeAsState(false) + val showLocationProviderDialog = + helper.locationProviderDialogViewModel.dialogVisibilityLiveData.observeAsState(false) val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList()) var isLoading by remember { mutableStateOf(true) } @@ -48,18 +52,23 @@ fun WearPermissionAppsScreen(helper: WearPermissionAppsHelper) { val showAlways = helper.showAlways() val chipsByCategory = helper.getChipsByCategory(categorizedApps.value, appPermissionUsages.value) - - WearPermissionAppsContent( - chipsByCategory, - showSystem.value, - hasSystemApps.value, - title, - subTitle, - showAlways, - isLoading, - helper.onShowSystemClick - ) - + Box(modifier = Modifier.fillMaxSize()) { + val dialogArgs = helper.locationProviderDialogViewModel.locationProviderInterceptDialogArgs + if (showLocationProviderDialog.value && dialogArgs != null) { + LocationProviderDialogScreen(dialogArgs) + } else { + WearPermissionAppsContent( + chipsByCategory = chipsByCategory, + showSystem = showSystem.value, + hasSystemApps = hasSystemApps.value, + title = title, + subtitle = subTitle, + showAlways = showAlways, + isLoading = isLoading, + onShowSystemClick = helper.onShowSystemClick + ) + } + } if (isLoading && categorizedApps.value.isNotEmpty()) { isLoading = false } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt index ead7f9503..2ade4863a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt @@ -29,9 +29,8 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory -import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled /** * This is a condensed version of @@ -53,8 +52,6 @@ class WearPermissionUsageDetailsFragment : Fragment() { } val showSystem = arguments?.getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false) ?: false - val show7Days = - arguments?.getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false) ?: false val factory = PermissionUsageDetailsViewModelFactory( @@ -63,9 +60,8 @@ class WearPermissionUsageDetailsFragment : Fragment() { permissionGroup ) val viewModel = - ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel::class.java) + ViewModelProvider(this, factory).get(BasePermissionUsageDetailsViewModel::class.java) viewModel.updateShowSystemAppsToggle(showSystem) - viewModel.updateShow7DaysToggle(is7DayToggleEnabled() && show7Days) return ComposeView(requireContext()).apply { setContent { WearPermissionUsageDetailsScreen(permissionGroup, viewModel) } @@ -79,7 +75,6 @@ class WearPermissionUsageDetailsFragment : Fragment() { fun newInstance( groupName: String?, showSystem: Boolean, - show7Days: Boolean ): WearPermissionUsageDetailsFragment { return WearPermissionUsageDetailsFragment().apply { val arguments = @@ -88,7 +83,6 @@ class WearPermissionUsageDetailsFragment : Fragment() { putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) } putBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, showSystem) - putBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, show7Days) } setArguments(arguments) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt index 3c62e5343..1259c1ab5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt @@ -33,8 +33,10 @@ import androidx.compose.ui.res.stringResource import androidx.wear.compose.material.ChipDefaults import androidx.wear.compose.material.MaterialTheme import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen import com.android.permissioncontroller.permission.utils.KotlinUtils @@ -43,10 +45,10 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils @Composable fun WearPermissionUsageDetailsScreen( permissionGroup: String, - viewModel: PermissionUsageDetailsViewModel + viewModel: BasePermissionUsageDetailsViewModel ) { val context = LocalContext.current - val uiData = viewModel.permissionUsagesDetailsInfoUiLiveData.observeAsState(null) + val uiData = viewModel.getPermissionUsagesDetailsInfoUiLiveData().observeAsState(null) val showSystem = viewModel.showSystemLiveData.observeAsState(false) var isLoading by remember { mutableStateOf(true) } @@ -56,7 +58,14 @@ fun WearPermissionUsageDetailsScreen( R.string.permission_group_usage_title, KotlinUtils.getPermGroupLabel(context, permissionGroup) ) - val hasSystemApps: Boolean = uiData.value?.containsSystemAppAccesses ?: false + + val hasSystemApps: Boolean = + if (uiData.value is PermissionUsageDetailsUiState.Success) { + (uiData.value as PermissionUsageDetailsUiState.Success).containsSystemAppUsage + } else { + false + } + val onShowSystemClick: (Boolean) -> Unit = { show -> run { viewModel.updateShowSystemAppsToggle(show) } } @@ -84,7 +93,11 @@ fun WearPermissionUsageDetailsScreen( } val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo> = - uiData.value?.appPermissionAccessUiInfoList ?: emptyList() + if (uiData.value is PermissionUsageDetailsUiState.Success) { + (uiData.value as PermissionUsageDetailsUiState.Success).appPermissionAccessUiInfoList + } else { + emptyList() + } WearPermissionUsageDetailsContent( title, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt index 8f8960269..d2832bc90 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt @@ -28,8 +28,6 @@ import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.Constants import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModelFactory -import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModel -import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModelFactory /** * This is a condensed version of @@ -38,27 +36,19 @@ import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionU */ @RequiresApi(Build.VERSION_CODES.S) class WearPermissionUsageFragment : Fragment() { - lateinit var wearViewModel: WearPermissionUsageViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val sessionId: Long = arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID - val factory = - PermissionUsageViewModelFactory(requireActivity().getApplication(), this, Bundle()) + + val factory = PermissionUsageViewModelFactory(requireActivity().application) val viewModel = ViewModelProvider(this, factory).get(PermissionUsageViewModel::class.java) - wearViewModel = - ViewModelProvider(this, WearPermissionUsageViewModelFactory(viewModel)) - .get(WearPermissionUsageViewModel::class.java) - viewModel.permissionUsagesUiLiveData.observe( - this, - wearViewModel::updatePermissionUsagesUiStateLiveData - ) return ComposeView(requireContext()).apply { - setContent { WearPermissionUsageScreen(sessionId, viewModel, wearViewModel) } + setContent { WearPermissionUsageScreen(sessionId, viewModel) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt index 62d0c6212..f83d3338d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt @@ -34,7 +34,6 @@ import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUs import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModel import com.android.permissioncontroller.permission.utils.Utils import java.text.Collator @@ -43,27 +42,21 @@ import java.text.Collator fun WearPermissionUsageScreen( sessionId: Long, viewModel: PermissionUsageViewModel, - wearViewModel: WearPermissionUsageViewModel ) { val context = LocalContext.current - val permissionUsagesUiData = wearViewModel.permissionUsagesUiStateLiveData.observeAsState(null) - val showSystem = wearViewModel.showSystemAppsLiveData.observeAsState(false) - val show7Days = wearViewModel.show7DaysLiveData.observeAsState(false) + val permissionUsagesUiData = viewModel.permissionUsagesUiLiveData.observeAsState(null) + val showSystem = viewModel.showSystemAppsLiveData.observeAsState(false) var isLoading by remember { mutableStateOf(true) } val isDataLoaded = permissionUsagesUiData.value is PermissionUsagesUiState.Success val hasSystemApps: Boolean = if (isDataLoaded) { val uiState = permissionUsagesUiData.value as PermissionUsagesUiState.Success - uiState.shouldShowSystemToggle + uiState.containsSystemAppUsage } else { false } - val onShowSystemClick: (Boolean) -> Unit = { show -> - run { - wearViewModel.updatePermissionUsagesUiStateLiveData(viewModel.updateShowSystem(show)) - wearViewModel.showSystemAppsLiveData.value = viewModel.getShowSystemApps() - } - } + + val onShowSystemClick: (Boolean) -> Unit = { show -> run { viewModel.updateShowSystem(show) } } val permissionGroupWithUsageCounts: Map<String, Int> = if (isDataLoaded) { @@ -87,7 +80,7 @@ fun WearPermissionUsageScreen( it.value, showSystem.value, sessionId, - show7Days.value + false, ) } .sortedWith { o1, o2 -> diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt index 77fbc811f..c07d2ba9e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt @@ -159,7 +159,6 @@ fun AlertContent( text = it, color = MaterialTheme.colors.onBackground, textAlign = TextAlign.Center, - maxLines = if (icon == null) 3 else 2, overflow = TextOverflow.Ellipsis, ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt index bcdf3b661..07bb88e80 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt @@ -32,47 +32,70 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextDecoration import androidx.wear.compose.material.MaterialTheme +import com.android.permissioncontroller.permission.ui.wear.WearUtils.capitalize const val CLICKABLE_SPAN_TAG = "CLICKABLE_SPAN_TAG" @Composable -fun AnnotatedText(text: CharSequence, style: TextStyle, modifier: Modifier = Modifier) { +fun AnnotatedText( + text: CharSequence, + style: TextStyle, + modifier: Modifier = Modifier, + shouldCapitalize: Boolean, +) { val onClickCallbacks = mutableMapOf<String, (View) -> Unit>() val context = LocalContext.current val listener = LinkInteractionListener { if (it is LinkAnnotation.Clickable) { - onClickCallbacks.get(it.tag)?.invoke(View(context)) + onClickCallbacks[it.tag]?.invoke(View(context)) } } val annotatedString = - spannableStringToAnnotatedString(text, onClickCallbacks, listener = listener) + spannableStringToAnnotatedString( + text, + shouldCapitalize, + onClickCallbacks, + listener = listener, + ) BasicText(text = annotatedString, style = style, modifier = modifier) } @Composable private fun spannableStringToAnnotatedString( text: CharSequence, + shouldCapitalize: Boolean, onClickCallbacks: MutableMap<String, (View) -> Unit>, spanColor: Color = MaterialTheme.colors.primary, - listener: LinkInteractionListener -) = - if (text is Spanned) { - buildAnnotatedString { - append((text.toString())) - for (span in text.getSpans(0, text.length, Any::class.java)) { - val start = text.getSpanStart(span) - val end = text.getSpanEnd(span) - when (span) { - is ClickableSpan -> - addClickableSpan(span, spanColor, start, end, onClickCallbacks, listener) - else -> addStyle(SpanStyle(), start, end) + listener: LinkInteractionListener, +): AnnotatedString { + val finalString = if (shouldCapitalize) text.toString().capitalize() else text.toString() + val annotatedString = + if (text is Spanned) { + buildAnnotatedString { + append(finalString) + for (span in text.getSpans(0, text.length, Any::class.java)) { + val start = text.getSpanStart(span) + val end = text.getSpanEnd(span) + when (span) { + is ClickableSpan -> + addClickableSpan( + span, + spanColor, + start, + end, + onClickCallbacks, + listener, + ) + else -> addStyle(SpanStyle(), start, end) + } } } + } else { + AnnotatedString(finalString) } - } else { - AnnotatedString(text.toString()) - } + return annotatedString +} private fun AnnotatedString.Builder.addClickableSpan( span: ClickableSpan, @@ -80,14 +103,10 @@ private fun AnnotatedString.Builder.addClickableSpan( start: Int, end: Int, onClickCallbacks: MutableMap<String, (View) -> Unit>, - listener: LinkInteractionListener + listener: LinkInteractionListener, ) { val key = "${CLICKABLE_SPAN_TAG}:$start:$end" onClickCallbacks[key] = span::onClick addLink(LinkAnnotation.Clickable(key, linkInteractionListener = listener), start, end) - addStyle( - SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline), - start, - end, - ) + addStyle(SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline), start, end) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt deleted file mode 100644 index 1394c56ea..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.ui.wear.elements - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.Dp -import androidx.wear.compose.material.Button -import androidx.wear.compose.material.ButtonColors -import androidx.wear.compose.material.ButtonDefaults -import androidx.wear.compose.material.ButtonDefaults.DefaultButtonSize -import androidx.wear.compose.material.ButtonDefaults.DefaultIconSize -import androidx.wear.compose.material.ButtonDefaults.LargeButtonSize -import androidx.wear.compose.material.ButtonDefaults.LargeIconSize -import androidx.wear.compose.material.ButtonDefaults.SmallButtonSize -import androidx.wear.compose.material.ButtonDefaults.SmallIconSize - -/** - * This component is an alternative to [Button], providing the following: - * - a convenient way of providing an icon and choosing its size from a range of sizes recommended - * by the Wear guidelines; - */ -@Composable -public fun Button( - imageVector: ImageVector, - contentDescription: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - colors: ButtonColors = ButtonDefaults.primaryButtonColors(), - buttonSize: ButtonSize = ButtonSize.Default, - iconRtlMode: IconRtlMode = IconRtlMode.Default, - enabled: Boolean = true -) { - Button( - icon = imageVector, - contentDescription = contentDescription, - onClick = onClick, - modifier = modifier, - colors = colors, - buttonSize = buttonSize, - iconRtlMode = iconRtlMode, - enabled = enabled - ) -} - -/** - * This component is an alternative to [Button], providing the following: - * - a convenient way of providing an icon and choosing its size from a range of sizes recommended - * by the Wear guidelines; - */ -@Composable -public fun Button( - @DrawableRes id: Int, - contentDescription: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - colors: ButtonColors = ButtonDefaults.primaryButtonColors(), - buttonSize: ButtonSize = ButtonSize.Default, - iconRtlMode: IconRtlMode = IconRtlMode.Default, - enabled: Boolean = true -) { - Button( - icon = id, - contentDescription = contentDescription, - onClick = onClick, - modifier = modifier, - colors = colors, - buttonSize = buttonSize, - iconRtlMode = iconRtlMode, - enabled = enabled - ) -} - -@Composable -internal fun Button( - icon: Any, - contentDescription: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - colors: ButtonColors = ButtonDefaults.primaryButtonColors(), - buttonSize: ButtonSize = ButtonSize.Default, - iconRtlMode: IconRtlMode = IconRtlMode.Default, - enabled: Boolean = true -) { - Button( - onClick = onClick, - modifier = modifier.size(buttonSize.tapTargetSize), - enabled = enabled, - colors = colors - ) { - val iconModifier = Modifier.size(buttonSize.iconSize).align(Alignment.Center) - - Icon( - icon = icon, - contentDescription = contentDescription, - modifier = iconModifier, - rtlMode = iconRtlMode - ) - } -} - -public sealed class ButtonSize(public val iconSize: Dp, public val tapTargetSize: Dp) { - public object Default : - ButtonSize(iconSize = DefaultIconSize, tapTargetSize = DefaultButtonSize) - - public object Large : ButtonSize(iconSize = LargeIconSize, tapTargetSize = LargeButtonSize) - public object Small : ButtonSize(iconSize = SmallIconSize, tapTargetSize = SmallButtonSize) - - /** - * Custom sizes should follow the - * [accessibility principles and guidance for touch targets](https://developer.android.com/training/wearables/accessibility#set-minimum). - */ - public data class Custom(val customIconSize: Dp, val customTapTargetSize: Dp) : - ButtonSize(iconSize = customIconSize, tapTargetSize = customTapTargetSize) -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt index ff3eddc65..40f097c67 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.Hyphens import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -53,7 +54,7 @@ import androidx.wear.compose.material.contentColorFor * by the Wear guidelines; */ @Composable -public fun Chip( +fun Chip( label: String, labelMaxLines: Int? = null, onClick: () -> Unit, @@ -130,7 +131,7 @@ public fun Chip( * by the Wear guidelines; */ @Composable -public fun Chip( +fun Chip( @StringRes labelId: Int, labelMaxLines: Int? = null, onClick: () -> Unit, @@ -167,7 +168,7 @@ public fun Chip( // Setting the color as per // https://source.corp.google.com/piper///depot/google3/java/com/google/android/clockwork/common/wearable/wearmaterial/button/res/color/wear_button_secondary_text_stateful.xml?q=wear_button_secondary_text_stateful @Composable -public fun Chip( +fun Chip( label: String, labelMaxLines: Int? = null, onClick: () -> Unit, @@ -192,7 +193,11 @@ public fun Chip( textAlign = if (hasSecondaryLabel || hasIcon) TextAlign.Start else TextAlign.Center, overflow = TextOverflow.Ellipsis, maxLines = labelMaxLines ?: if (hasSecondaryLabel) 1 else 2, - style = MaterialTheme.typography.button.copy(fontWeight = FontWeight.W600) + style = + MaterialTheme.typography.button.copy( + fontWeight = FontWeight.W600, + hyphens = Hyphens.Auto + ) ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt index 259e1b0b2..0a2a3937c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.Hyphens import androidx.compose.ui.unit.dp import androidx.wear.compose.material.LocalContentColor import androidx.wear.compose.material.LocalTextStyle @@ -73,7 +74,10 @@ fun ListHeader( CompositionLocalProvider( LocalContentColor provides contentColor, LocalTextStyle provides - MaterialTheme.typography.title3.copy(fontWeight = FontWeight.W600), + MaterialTheme.typography.title3.copy( + fontWeight = FontWeight.W600, + hyphens = Hyphens.Auto + ), ) { content() } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt index 1700c719f..d1b7e899b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt @@ -63,7 +63,10 @@ import androidx.wear.compose.material.TimeText import androidx.wear.compose.material.Vignette import androidx.wear.compose.material.VignettePosition import androidx.wear.compose.material.scrollAway +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rotaryWithScroll +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme /** @@ -74,6 +77,7 @@ import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionT */ @Composable fun ScrollableScreen( + materialUIVersion: WearPermissionMaterialUIVersion = MATERIAL2_5, showTimeText: Boolean = true, title: String? = null, subtitle: CharSequence? = null, @@ -88,18 +92,23 @@ fun ScrollableScreen( val state = rememberSwipeToDismissBoxState() LaunchedEffect(state.currentValue) { + // If the swipe is complete if (state.currentValue == SwipeToDismissValue.Dismissed) { + // pop the top fragment immediately or dismiss activity. dismiss(activity) + // Set dismissed state as true dismissed = true + // Set swipe box back to starting position(that is cancelled swipe effect) to + // show loading indicator while fragment dismisses. + // For some reason fragment `popBackImmediate` takes few secs at times. state.snapTo(SwipeToDismissValue.Default) } } - // To support Swipe-dismiss effect, - // add the view to SwipeToDismissBox if the screen is not on the top fragment. if (getBackStackEntryCount(activity) > 0) { SwipeToDismissBox(state = state) { isBackground -> - Scaffold( + WearPermissionScaffold( + materialUIVersion, showTimeText, title, subtitle, @@ -107,11 +116,12 @@ fun ScrollableScreen( isLoading = isLoading || isBackground || dismissed, content, titleTestTag, - subtitleTestTag + subtitleTestTag, ) } } else { - Scaffold( + WearPermissionScaffold( + materialUIVersion, showTimeText, title, subtitle, @@ -119,13 +129,13 @@ fun ScrollableScreen( isLoading, content, titleTestTag, - subtitleTestTag + subtitleTestTag, ) } } @Composable -internal fun Scaffold( +internal fun Wear2Scaffold( showTimeText: Boolean, title: String?, subtitle: CharSequence?, @@ -161,14 +171,14 @@ internal fun Scaffold( start = titleHorizontalPadding, top = 4.dp, bottom = titleBottomPadding, - end = titleHorizontalPadding + end = titleHorizontalPadding, ) val subTitlePaddingValues = PaddingValues( start = subtitleHorizontalPadding, top = 4.dp, bottom = subtitleBottomPadding, - end = subtitleHorizontalPadding + end = subtitleHorizontalPadding, ) val initialCenterIndex = 0 val centerHeightDp = Dp(LocalConfiguration.current.screenHeightDp / 2.0f) @@ -187,19 +197,24 @@ internal fun Scaffold( modifier = Modifier.rotaryWithScroll( scrollableState = listState, - focusRequester = focusRequester + focusRequester = focusRequester, ), timeText = { if (showTimeText && !isLoading) { TimeText( modifier = Modifier.scrollAway(listState, initialCenterIndex, scrollAwayOffset) - .padding(top = timeTextTopPadding), + .padding(top = timeTextTopPadding) ) } }, vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }, - positionIndicator = { PositionIndicator(scalingLazyListState = listState) } + positionIndicator = + if (!isLoading) { + { PositionIndicator(scalingLazyListState = listState) } + } else { + null + }, ) { Box(modifier = Modifier.fillMaxSize()) { if (isLoading) { @@ -216,8 +231,8 @@ internal fun Scaffold( start = scrollContentHorizontalPadding, end = scrollContentHorizontalPadding, top = scrollContentTopPadding, - bottom = scrollContentBottomPadding - ) + bottom = scrollContentBottomPadding, + ), ) { staticItem() image?.let { @@ -229,7 +244,7 @@ internal fun Scaffold( painter = painterResource(id = image), contentDescription = null, contentScale = ContentScale.Crop, - modifier = imageModifier + modifier = imageModifier, ) } is Drawable -> @@ -238,7 +253,7 @@ internal fun Scaffold( painter = rememberDrawablePainter(image), contentDescription = null, contentScale = ContentScale.Crop, - modifier = imageModifier + modifier = imageModifier, ) } else -> {} @@ -254,7 +269,7 @@ internal fun Scaffold( Text( text = title, textAlign = TextAlign.Center, - modifier = modifier + modifier = modifier, ) } } @@ -273,6 +288,7 @@ internal fun Scaffold( color = MaterialTheme.colors.onSurfaceVariant ), modifier = modifier, + shouldCapitalize = true, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt index a21a9d015..4f4201748 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt @@ -29,11 +29,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.semantics.role -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.wear.compose.material.ChipDefaults @@ -44,7 +39,6 @@ import androidx.wear.compose.material.ToggleChip import androidx.wear.compose.material.ToggleChipColors import androidx.wear.compose.material.ToggleChipDefaults import androidx.wear.compose.material.contentColorFor -import com.android.permissioncontroller.R /** * This component is an alternative to [ToggleChip], providing the following: @@ -67,7 +61,7 @@ fun ToggleChip( secondaryLabelMaxLine: Int? = null, colors: ToggleChipColors = ToggleChipDefaults.toggleChipColors(), enabled: Boolean = true, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { val hasSecondaryLabel = secondaryLabel != null @@ -78,7 +72,7 @@ fun ToggleChip( textAlign = TextAlign.Start, overflow = TextOverflow.Ellipsis, maxLines = labelMaxLine ?: if (hasSecondaryLabel) 1 else 2, - style = MaterialTheme.typography.button + style = MaterialTheme.typography.button, ) } @@ -89,7 +83,7 @@ fun ToggleChip( text = secondaryLabel, overflow = TextOverflow.Ellipsis, maxLines = secondaryLabelMaxLine ?: 1, - style = MaterialTheme.typography.caption2 + style = MaterialTheme.typography.caption2, ) } } @@ -110,7 +104,7 @@ fun ToggleChip( IconRtlMode.Mirrored } else { IconRtlMode.Default - } + }, ) } @@ -123,42 +117,23 @@ fun ToggleChip( tint = iconColor, contentDescription = null, modifier = Modifier.size(ChipDefaults.IconSize).clip(CircleShape), - rtlMode = iconRtlMode + rtlMode = iconRtlMode, ) } } } - val semanticsRole = - when (toggleControl) { - ToggleChipToggleControl.Switch -> Role.Switch - ToggleChipToggleControl.Radio -> Role.RadioButton - ToggleChipToggleControl.Checkbox -> Role.Checkbox - } - - val stateDescriptionSemantics = - stringResource( - if (checked) { - R.string.on - } else { - R.string.off - } - ) ToggleChip( checked = checked, onCheckedChange = onCheckedChanged, label = labelParam, toggleControl = toggleControlParam, - modifier = - modifier.fillMaxWidth().semantics { - role = semanticsRole - stateDescription = stateDescriptionSemantics - }, + modifier = modifier.fillMaxWidth().toggleControlSemantics(toggleControl, checked), appIcon = iconParam, secondaryLabel = secondaryLabelParam, colors = colors, enabled = enabled, - interactionSource = interactionSource + interactionSource = interactionSource, ) } @@ -198,7 +173,7 @@ fun toggleChipDisabledColors(): ToggleChipColors { uncheckedSecondaryContentColor = uncheckedSecondaryContentColor.copy(alpha = ContentAlpha.disabled), uncheckedToggleControlColor = - uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled) + uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled), ) } @@ -236,6 +211,6 @@ fun toggleChipBackgroundColors(): ToggleChipColors { uncheckedEndBackgroundColor = uncheckedEndBackgroundColor, uncheckedContentColor = uncheckedContentColor, uncheckedSecondaryContentColor = uncheckedSecondaryContentColor, - uncheckedToggleControlColor = uncheckedToggleControlColor + uncheckedToggleControlColor = uncheckedToggleControlColor, ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt index a4ce4e764..b6f6db4d3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt @@ -16,8 +16,43 @@ package com.android.permissioncontroller.permission.ui.wear.elements -public enum class ToggleChipToggleControl { +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription +import com.android.permissioncontroller.R + +enum class ToggleChipToggleControl { Switch, Radio, - Checkbox + Checkbox, +} + +@Composable +fun Modifier.toggleControlSemantics( + toggleControl: ToggleChipToggleControl, + checked: Boolean, +): Modifier { + val semanticsRole = + when (toggleControl) { + ToggleChipToggleControl.Switch -> Role.Switch + ToggleChipToggleControl.Radio -> Role.RadioButton + ToggleChipToggleControl.Checkbox -> Role.Checkbox + } + val stateDescriptionSemantics = + stringResource( + if (checked) { + R.string.on + } else { + R.string.off + } + ) + + return semantics { + role = semanticsRole + stateDescription = stateDescriptionSemantics + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt new file mode 100644 index 000000000..1d660ca35 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.wear.elements.material3 + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.requiredSizeIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.Hyphens +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material3.Button +import androidx.wear.compose.material3.ButtonColors +import androidx.wear.compose.material3.ButtonDefaults +import androidx.wear.compose.material3.LocalTextConfiguration +import androidx.wear.compose.material3.LocalTextStyle +import androidx.wear.compose.material3.Text +import com.android.permissioncontroller.permission.ui.wear.elements.Chip +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion + +/** + * This component is wrapper on material Button component + * 1. It takes icon, primary, secondary label resources and construct them applying permission app + * defaults + */ +@Composable +fun WearPermissionButton( + label: String, + modifier: Modifier = Modifier, + materialUIVersion: WearPermissionMaterialUIVersion = + WearPermissionMaterialUIVersion.MATERIAL2_5, + iconBuilder: WearPermissionIconBuilder? = null, + labelMaxLines: Int? = null, + secondaryLabel: String? = null, + secondaryLabelMaxLines: Int? = null, + onClick: () -> Unit, + enabled: Boolean = true, + style: WearPermissionButtonStyle = WearPermissionButtonStyle.Secondary, +) { + if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) { + Chip( + label = label, + labelMaxLines = labelMaxLines, + onClick = onClick, + modifier = modifier, + secondaryLabel = secondaryLabel, + secondaryLabelMaxLines = secondaryLabelMaxLines, + icon = { iconBuilder?.build() }, + largeIcon = false, + colors = style.material2ChipColors(), + enabled = enabled, + ) + } else { + WearPermissionButtonInternal( + iconBuilder = iconBuilder, + label = label, + labelMaxLines = labelMaxLines, + secondaryLabel = secondaryLabel, + secondaryLabelMaxLines = secondaryLabelMaxLines, + onClick = onClick, + modifier = modifier, + enabled = enabled, + colors = style.material3ButtonColors(), + ) + } +} + +@Composable +internal fun WearPermissionButtonInternal( + modifier: Modifier = Modifier, + label: String? = null, + iconBuilder: WearPermissionIconBuilder? = null, + labelMaxLines: Int? = null, + secondaryLabel: String? = null, + secondaryLabelMaxLines: Int? = null, + onClick: () -> Unit, + enabled: Boolean = true, + colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(), + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + requiresMinimumHeight: Boolean = true, +) { + val minHeight: Dp = + if (requiresMinimumHeight) { + 0.dp + } else { + 1.dp + } + val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } } + val labelParam: (@Composable RowScope.() -> Unit)? = + label?.let { + { + Text( + text = label, + modifier = Modifier.fillMaxWidth(), + maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines, + style = + LocalTextStyle.current.copy( + fontWeight = FontWeight.W600, + hyphens = Hyphens.Auto, + ), + ) + } + } + + val secondaryLabelParam: (@Composable RowScope.() -> Unit)? = + secondaryLabel?.let { + { + Text( + text = secondaryLabel, + modifier = Modifier.fillMaxWidth(), + maxLines = secondaryLabelMaxLines ?: LocalTextConfiguration.current.maxLines, + ) + } + } + + Button( + icon = iconParam, + label = labelParam ?: {}, + secondaryLabel = secondaryLabelParam, + enabled = enabled, + onClick = onClick, + modifier = modifier.requiredSizeIn(minHeight = minHeight).fillMaxWidth(), + contentPadding = contentPadding, + colors = colors, + ) +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt new file mode 100644 index 000000000..504c69bb0 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.wear.elements.material3 + +import androidx.compose.runtime.Composable +import androidx.wear.compose.material.ChipColors +import androidx.wear.compose.material.ChipDefaults +import androidx.wear.compose.material3.ButtonColors +import androidx.wear.compose.material3.ButtonDefaults +import com.android.permissioncontroller.permission.ui.wear.elements.chipDefaultColors +import com.android.permissioncontroller.permission.ui.wear.elements.chipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.DisabledLike +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Primary +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Secondary +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Transparent + +/** + * This component is wrapper on material control colors, It applies the right colors based material + * ui version. + */ +enum class WearPermissionButtonStyle { + Primary, + Secondary, + Transparent, + DisabledLike, +} + +@Composable +internal fun WearPermissionButtonStyle.material2ChipColors(): ChipColors { + return when (this) { + Primary -> chipDefaultColors() + Secondary -> ChipDefaults.secondaryChipColors() + Transparent -> ChipDefaults.childChipColors() + DisabledLike -> chipDisabledColors() + } +} + +@Composable +internal fun WearPermissionButtonStyle.material3ButtonColors(): ButtonColors { + return when (this) { + Primary -> ButtonDefaults.buttonColors() + Secondary -> ButtonDefaults.filledTonalButtonColors() + Transparent -> ButtonDefaults.childButtonColors() + DisabledLike -> ButtonDefaults.disabledLikeColors() + } +} + +@Composable +private fun ButtonDefaults.disabledLikeColors() = + filledTonalButtonColors().run { + ButtonColors( + containerPainter = disabledContainerPainter, + contentColor = disabledContentColor, + secondaryContentColor = disabledSecondaryContentColor, + iconColor = disabledIconColor, + disabledContainerPainter = disabledContainerPainter, + disabledContentColor = disabledContentColor, + disabledSecondaryContentColor = disabledSecondaryContentColor, + disabledIconColor = disabledIconColor, + ) + } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt new file mode 100644 index 000000000..b7521d073 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionIconBuilder.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.wear.elements.material3 + +import android.graphics.drawable.Drawable +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.wear.compose.material3.Icon +import androidx.wear.compose.material3.IconButtonDefaults +import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter + +/** + * This class simplifies the construction of icons with various attributes like resource type, + * content description, modifier, and tint. It supports different icon resource types, including: + * - ImageVector + * - Resource ID (Int) + * - Drawable + * - ImageBitmap + * + * Usage: + * ``` + * val icon = WearPermissionIconBuilder.builder(IconResourceId) + * .contentDescription("Location Permission") + * .modifier(Modifier.size(24.dp)) + * .tint(Color.Red) + * .build() + * ``` + * + * Note: This builder uses a private constructor and is initialized through the `builder()` + * companion object method. + */ +class WearPermissionIconBuilder private constructor() { + var iconResource: Any? = null + private set + + var contentDescription: String? = null + private set + + var modifier: Modifier = Modifier.size(IconButtonDefaults.LargeIconSize) + private set + + var tint: Color = Color.Unspecified + private set + + fun contentDescription(description: String?): WearPermissionIconBuilder { + contentDescription = description + return this + } + + fun modifier(modifier: Modifier): WearPermissionIconBuilder { + this.modifier then modifier + return this + } + + fun tint(tint: Color): WearPermissionIconBuilder { + this.tint = tint + return this + } + + @Composable + fun build() { + when (iconResource) { + is ImageVector -> Icon(iconResource as ImageVector, contentDescription, modifier, tint) + is Int -> + Icon(painterResource(id = iconResource as Int), contentDescription, modifier, tint) + + is Drawable -> + Icon( + rememberDrawablePainter(iconResource as Drawable), + contentDescription, + modifier, + tint, + ) + + is ImageBitmap -> Icon(iconResource as ImageBitmap, contentDescription, modifier, tint) + else -> throw IllegalArgumentException("Type not supported.") + } + } + + companion object { + fun builder(icon: Any) = WearPermissionIconBuilder().apply { iconResource = icon } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt new file mode 100644 index 000000000..10125c873 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.wear.elements.material3 + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material3.ButtonDefaults +import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion + +/** This component is creates a transparent styled button to use as a list footer. */ +@Composable +fun WearPermissionListFooter( + materialUIVersion: WearPermissionMaterialUIVersion, + label: String, + iconBuilder: WearPermissionIconBuilder? = null, + onClick: (() -> Unit) = {}, +) { + if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) { + ListFooter( + description = label, + iconRes = iconBuilder?.let { it.iconResource as Int }, + onClick = onClick, + ) + } else { + WearPermissionButtonInternal( + iconBuilder = iconBuilder, + secondaryLabel = label, + secondaryLabelMaxLines = Int.MAX_VALUE, + onClick = onClick, + contentPadding = PaddingValues(0.dp), + colors = ButtonDefaults.childButtonColors(), + requiresMinimumHeight = false, + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt new file mode 100644 index 000000000..bd7636273 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt @@ -0,0 +1,298 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.wear.elements.material3 + +import android.graphics.drawable.Drawable +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeightIn +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.wear.compose.foundation.ScrollInfoProvider +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope +import androidx.wear.compose.material3.AppScaffold +import androidx.wear.compose.material3.CircularProgressIndicator +import androidx.wear.compose.material3.ListHeader +import androidx.wear.compose.material3.MaterialTheme +import androidx.wear.compose.material3.ScreenScaffold +import androidx.wear.compose.material3.ScrollIndicator +import androidx.wear.compose.material3.Text +import androidx.wear.compose.material3.TimeText +import com.android.permissioncontroller.permission.ui.wear.elements.AnnotatedText +import com.android.permissioncontroller.permission.ui.wear.elements.Wear2Scaffold +import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumn +import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState +import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberResponsiveColumnState +import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme + +/** + * This component is wrapper on material scaffold component. It helps with time text, scroll + * indicator and standard list elements like title, icon and subtitle. + */ +@Composable +internal fun WearPermissionScaffold( + materialUIVersion: WearPermissionMaterialUIVersion = MATERIAL2_5, + showTimeText: Boolean, + title: String?, + subtitle: CharSequence?, + image: Any?, + isLoading: Boolean, + content: ScalingLazyListScope.() -> Unit, + titleTestTag: String? = null, + subtitleTestTag: String? = null, +) { + + if (materialUIVersion == MATERIAL2_5) { + Wear2Scaffold( + showTimeText, + title, + subtitle, + image, + isLoading, + content, + titleTestTag, + subtitleTestTag, + ) + } else { + WearPermissionScaffoldInternal( + showTimeText, + title, + subtitle, + image, + isLoading, + content, + titleTestTag, + subtitleTestTag, + ) + } +} + +@Composable +private fun WearPermissionScaffoldInternal( + showTimeText: Boolean, + title: String?, + subtitle: CharSequence?, + image: Any?, + isLoading: Boolean, + content: ScalingLazyListScope.() -> Unit, + titleTestTag: String? = null, + subtitleTestTag: String? = null, +) { + val screenWidth = LocalConfiguration.current.screenWidthDp + val screenHeight = LocalConfiguration.current.screenHeightDp + val paddingDefaults = + WearPermissionScaffoldPaddingDefaults( + screenWidth = screenWidth, + screenHeight = screenHeight, + titleNeedsLargePadding = subtitle == null, + ) + val columnState = + rememberResponsiveColumnState(contentPadding = { paddingDefaults.scrollContentPadding }) + WearPermissionTheme(version = WearPermissionMaterialUIVersion.MATERIAL3) { + AppScaffold(timeText = wearPermissionTimeText(showTimeText && !isLoading)) { + ScreenScaffold( + scrollInfoProvider = ScrollInfoProvider(columnState.state), + scrollIndicator = wearPermissionScrollIndicator(!isLoading, columnState), + ) { + Box(modifier = Modifier.fillMaxSize()) { + if (isLoading) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } else { + ScrollingView( + columnState = columnState, + icon = painterFromImage(image), + title = title, + titleTestTag = titleTestTag, + titlePaddingValues = paddingDefaults.titlePaddingValues, + subtitle = subtitle, + subtitleTestTag = subtitleTestTag, + subTitlePaddingValues = paddingDefaults.subTitlePaddingValues, + content = content, + ) + } + } + } + } + } +} + +private class WearPermissionScaffoldPaddingDefaults( + screenWidth: Int, + screenHeight: Int, + titleNeedsLargePadding: Boolean, +) { + private val firstSpacerItemHeight = 0.dp + private val scrollContentHorizontalPadding = (screenWidth * 0.052).dp + private val titleHorizontalPadding = (screenWidth * 0.0884).dp + private val subtitleHorizontalPadding = (screenWidth * 0.0416).dp + private val scrollContentTopPadding = (screenHeight * 0.1456).dp - firstSpacerItemHeight + private val scrollContentBottomPadding = (screenHeight * 0.3636).dp + private val defaultItemPadding = 4.dp + private val largeItemPadding = 8.dp + val titlePaddingValues = + PaddingValues( + start = titleHorizontalPadding, + top = defaultItemPadding, + bottom = if (titleNeedsLargePadding) largeItemPadding else defaultItemPadding, + end = titleHorizontalPadding, + ) + val subTitlePaddingValues = + PaddingValues( + start = subtitleHorizontalPadding, + top = defaultItemPadding, + bottom = largeItemPadding, + end = subtitleHorizontalPadding, + ) + val scrollContentPadding = + PaddingValues( + start = scrollContentHorizontalPadding, + end = scrollContentHorizontalPadding, + top = scrollContentTopPadding, + bottom = scrollContentBottomPadding, + ) +} + +@Composable +private fun BoxScope.ScrollingView( + columnState: ScalingLazyColumnState, + icon: Painter?, + title: String?, + titleTestTag: String?, + subtitle: CharSequence?, + subtitleTestTag: String?, + titlePaddingValues: PaddingValues, + subTitlePaddingValues: PaddingValues, + content: ScalingLazyListScope.() -> Unit, +) { + ScalingLazyColumn(columnState = columnState) { + iconItem(icon, Modifier.size(24.dp)) + titleItem(text = title, testTag = titleTestTag, contentPaddingValues = titlePaddingValues) + subtitleItem( + text = subtitle, + testTag = subtitleTestTag, + modifier = Modifier.align(Alignment.Center).padding(subTitlePaddingValues), + ) + content() + } +} + +private fun wearPermissionTimeText(showTime: Boolean): @Composable () -> Unit { + return if (showTime) { + { TimeText { time() } } + } else { + {} + } +} + +private fun wearPermissionScrollIndicator( + showIndicator: Boolean, + columnState: ScalingLazyColumnState, +): @Composable (BoxScope.() -> Unit)? { + return if (showIndicator) { + { + ScrollIndicator( + modifier = Modifier.align(Alignment.CenterEnd), + state = columnState.state, + ) + } + } else { + null + } +} + +@Composable +private fun painterFromImage(image: Any?): Painter? { + return when (image) { + is Int -> painterResource(id = image) + is Drawable -> rememberDrawablePainter(image) + else -> null + } +} + +private fun Modifier.optionalTestTag(tag: String?): Modifier { + if (tag == null) { + return this + } + return this then testTag(tag) +} + +private fun ScalingLazyListScope.iconItem(painter: Painter?, modifier: Modifier = Modifier) = + painter?.let { + item { + Image( + painter = it, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = modifier, + ) + } + } + +private fun ScalingLazyListScope.titleItem( + text: String?, + testTag: String?, + contentPaddingValues: PaddingValues, + modifier: Modifier = Modifier, +) = + text?.let { + item { + ListHeader( + modifier = modifier.requiredHeightIn(1.dp), // We do not want default min height + contentPadding = contentPaddingValues, + ) { + Text( + text = it, + textAlign = TextAlign.Center, + modifier = Modifier.optionalTestTag(testTag), + ) + } + } + } + +private fun ScalingLazyListScope.subtitleItem( + text: CharSequence?, + testTag: String?, + modifier: Modifier = Modifier, +) = + text?.let { + item { + AnnotatedText( + text = it, + style = + MaterialTheme.typography.bodyMedium.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant + ), + modifier = modifier.optionalTestTag(testTag), + shouldCapitalize = true, + ) + } + } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt new file mode 100644 index 000000000..4a139f91f --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt @@ -0,0 +1,165 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.wear.elements.material3 + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.wear.compose.material3.CheckboxButton +import androidx.wear.compose.material3.LocalTextConfiguration +import androidx.wear.compose.material3.RadioButton +import androidx.wear.compose.material3.SwitchButton +import androidx.wear.compose.material3.Text +import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip +import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.toggleControlSemantics +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion + +/** + * The custom component is a wrapper on different material3 toggle controls. + * 1. It provides an unified interface for RadioButton,CheckButton and SwitchButton. + * 2. It takes icon, primary, secondary label resources and construct them applying permission app + * defaults + * 3. Applies custom semantics for based on the toggle control type + */ +@Composable +fun WearPermissionToggleControl( + toggleControl: ToggleChipToggleControl, + label: String, + checked: Boolean, + onCheckedChanged: (Boolean) -> Unit, + modifier: Modifier = Modifier, + labelMaxLines: Int? = null, + materialUIVersion: WearPermissionMaterialUIVersion = + WearPermissionMaterialUIVersion.MATERIAL2_5, + iconBuilder: WearPermissionIconBuilder? = null, + secondaryLabel: String? = null, + secondaryLabelMaxLines: Int? = null, + enabled: Boolean = true, + style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default, +) { + if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) { + ToggleChip( + toggleControl = toggleControl, + label = label, + labelMaxLine = labelMaxLines, + checked = checked, + onCheckedChanged = onCheckedChanged, + modifier = modifier, + icon = iconBuilder?.iconResource, + secondaryLabel = secondaryLabel, + secondaryLabelMaxLine = secondaryLabelMaxLines, + enabled = enabled, + colors = style.material2ToggleControlColors(), + ) + } else { + WearPermissionToggleControlInternal( + label = label, + toggleControl = toggleControl, + checked = checked, + onCheckedChanged = onCheckedChanged, + modifier = modifier, + iconBuilder = iconBuilder, + labelMaxLines = labelMaxLines, + secondaryLabel = secondaryLabel, + secondaryLabelMaxLines = secondaryLabelMaxLines, + enabled = enabled, + style = style, + ) + } +} + +@Composable +private fun WearPermissionToggleControlInternal( + label: String, + toggleControl: ToggleChipToggleControl, + checked: Boolean, + onCheckedChanged: (Boolean) -> Unit, + modifier: Modifier = Modifier, + iconBuilder: WearPermissionIconBuilder? = null, + labelMaxLines: Int? = null, + secondaryLabel: String? = null, + secondaryLabelMaxLines: Int? = null, + enabled: Boolean = true, + style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default, +) { + val labelParam: (@Composable RowScope.() -> Unit) = { + Text( + text = label, + modifier = Modifier.fillMaxWidth(), + maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines, + ) + } + + val secondaryLabelParam: (@Composable RowScope.() -> Unit)? = + secondaryLabel?.let { + { + Text( + text = it, + modifier = Modifier.fillMaxWidth(), + maxLines = secondaryLabelMaxLines ?: LocalTextConfiguration.current.maxLines, + ) + } + } + + val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } } + + val updatedModifier = + modifier + .fillMaxWidth() + // .heightIn(min = 58.dp) // TODO(b/370783358): This should be a overlaid value + .toggleControlSemantics(toggleControl, checked) + + when (toggleControl) { + ToggleChipToggleControl.Radio -> + RadioButton( + selected = checked, + onSelect = { onCheckedChanged(true) }, + modifier = updatedModifier, + enabled = enabled, + icon = iconParam, + secondaryLabel = secondaryLabelParam, + label = labelParam, + colors = style.radioButtonColorScheme(), + ) + + ToggleChipToggleControl.Checkbox -> + CheckboxButton( + checked = checked, + onCheckedChange = onCheckedChanged, + modifier = updatedModifier, + enabled = enabled, + icon = iconParam, + secondaryLabel = secondaryLabelParam, + label = labelParam, + colors = style.checkboxColorScheme(), + ) + + ToggleChipToggleControl.Switch -> + SwitchButton( + checked = checked, + onCheckedChange = onCheckedChanged, + modifier = updatedModifier, + enabled = enabled, + icon = iconParam, + secondaryLabel = secondaryLabelParam, + label = labelParam, + colors = style.switchButtonColorScheme(), + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt new file mode 100644 index 000000000..b5746f019 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt @@ -0,0 +1,158 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.wear.elements.material3 + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.wear.compose.material.ToggleChipColors +import androidx.wear.compose.material.ToggleChipDefaults.toggleChipColors +import androidx.wear.compose.material3.CheckboxButtonColors +import androidx.wear.compose.material3.CheckboxButtonDefaults.checkboxButtonColors +import androidx.wear.compose.material3.RadioButtonColors +import androidx.wear.compose.material3.RadioButtonDefaults.radioButtonColors +import androidx.wear.compose.material3.SwitchButtonColors +import androidx.wear.compose.material3.SwitchButtonDefaults.switchButtonColors +import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors +import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors + +/** + * Defines toggle control styles, It helps in setting the right colors scheme to a toggle control. + */ +enum class WearPermissionToggleControlStyle { + Default, + Transparent, + DisabledLike, +} + +@Composable +internal fun WearPermissionToggleControlStyle.radioButtonColorScheme(): RadioButtonColors { + return when (this) { + WearPermissionToggleControlStyle.Default -> radioButtonColors() + WearPermissionToggleControlStyle.Transparent -> radioButtonTransparentColors() + WearPermissionToggleControlStyle.DisabledLike -> radioButtonDisabledLikeColors() + } +} + +@Composable +internal fun WearPermissionToggleControlStyle.checkboxColorScheme(): CheckboxButtonColors { + return when (this) { + WearPermissionToggleControlStyle.Default -> checkboxButtonColors() + WearPermissionToggleControlStyle.Transparent -> checkButtonTransparentColors() + WearPermissionToggleControlStyle.DisabledLike -> checkboxDisabledLikeColors() + } +} + +@Composable +internal fun WearPermissionToggleControlStyle.switchButtonColorScheme(): SwitchButtonColors { + return when (this) { + WearPermissionToggleControlStyle.Default -> switchButtonColors() + WearPermissionToggleControlStyle.Transparent -> switchButtonTransparentColors() + WearPermissionToggleControlStyle.DisabledLike -> switchButtonDisabledLikeColors() + } +} + +@Composable +internal fun WearPermissionToggleControlStyle.material2ToggleControlColors(): ToggleChipColors { + return when (this) { + WearPermissionToggleControlStyle.Default -> toggleChipColors() + WearPermissionToggleControlStyle.Transparent -> toggleChipBackgroundColors() + WearPermissionToggleControlStyle.DisabledLike -> toggleChipDisabledColors() + } +} + +@Composable +private fun checkButtonTransparentColors() = + checkboxButtonColors( + checkedContainerColor = Color.Transparent, + uncheckedContainerColor = Color.Transparent, + disabledCheckedContainerColor = Color.Transparent, + disabledUncheckedContainerColor = Color.Transparent, + ) + +@Composable +private fun radioButtonTransparentColors() = + radioButtonColors( + selectedContainerColor = Color.Transparent, + unselectedContainerColor = Color.Transparent, + disabledSelectedContainerColor = Color.Transparent, + disabledUnselectedContainerColor = Color.Transparent, + ) + +@Composable +private fun switchButtonTransparentColors() = + switchButtonColors( + checkedContainerColor = Color.Transparent, + uncheckedContainerColor = Color.Transparent, + disabledCheckedContainerColor = Color.Transparent, + disabledUncheckedContainerColor = Color.Transparent, + ) + +@Composable +private fun checkboxDisabledLikeColors(): CheckboxButtonColors { + val defaultColors = checkboxButtonColors() + return checkboxButtonColors( + checkedContainerColor = defaultColors.disabledCheckedContainerColor, + checkedContentColor = defaultColors.disabledCheckedContentColor, + checkedSecondaryContentColor = defaultColors.disabledCheckedSecondaryContentColor, + checkedIconColor = defaultColors.disabledCheckedIconColor, + checkedBoxColor = defaultColors.disabledCheckedBoxColor, + checkedCheckmarkColor = defaultColors.disabledCheckedCheckmarkColor, + uncheckedContainerColor = defaultColors.disabledUncheckedContainerColor, + uncheckedContentColor = defaultColors.disabledUncheckedContentColor, + uncheckedSecondaryContentColor = defaultColors.disabledUncheckedSecondaryContentColor, + uncheckedIconColor = defaultColors.disabledUncheckedIconColor, + uncheckedBoxColor = defaultColors.disabledUncheckedBoxColor, + ) +} + +@Composable +private fun radioButtonDisabledLikeColors(): RadioButtonColors { + val defaultColors = radioButtonColors() + return radioButtonColors( + selectedContainerColor = defaultColors.disabledSelectedContainerColor, + selectedContentColor = defaultColors.disabledSelectedContentColor, + selectedSecondaryContentColor = defaultColors.disabledSelectedSecondaryContentColor, + selectedIconColor = defaultColors.disabledSelectedIconColor, + selectedControlColor = defaultColors.disabledSelectedControlColor, + unselectedContentColor = defaultColors.disabledUnselectedContentColor, + unselectedContainerColor = defaultColors.disabledUnselectedContainerColor, + unselectedSecondaryContentColor = defaultColors.disabledUnselectedSecondaryContentColor, + unselectedIconColor = defaultColors.disabledUnselectedIconColor, + unselectedControlColor = defaultColors.disabledUnselectedControlColor, + ) +} + +@Composable +private fun switchButtonDisabledLikeColors(): SwitchButtonColors { + val defaultColors = switchButtonColors() + return switchButtonColors( + checkedContainerColor = defaultColors.disabledCheckedContainerColor, + checkedContentColor = defaultColors.disabledCheckedContentColor, + checkedSecondaryContentColor = defaultColors.disabledCheckedSecondaryContentColor, + checkedIconColor = defaultColors.disabledCheckedIconColor, + checkedThumbColor = defaultColors.disabledCheckedThumbColor, + checkedThumbIconColor = defaultColors.disabledCheckedThumbIconColor, + checkedTrackColor = defaultColors.disabledCheckedTrackColor, + checkedTrackBorderColor = defaultColors.disabledCheckedTrackBorderColor, + uncheckedContainerColor = defaultColors.disabledUncheckedContainerColor, + uncheckedContentColor = defaultColors.disabledUncheckedContentColor, + uncheckedSecondaryContentColor = defaultColors.disabledUncheckedSecondaryContentColor, + uncheckedIconColor = defaultColors.disabledUncheckedIconColor, + uncheckedThumbColor = defaultColors.disabledUncheckedThumbColor, + uncheckedTrackColor = defaultColors.checkedTrackColor.run { copy(alpha = alpha * 0.12f) }, + uncheckedTrackBorderColor = defaultColors.disabledUncheckedTrackBorderColor, + ) +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearEnhancedConfirmationViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearEnhancedConfirmationViewModel.kt index 2a7df54ca..f4d5f29eb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearEnhancedConfirmationViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearEnhancedConfirmationViewModel.kt @@ -68,9 +68,8 @@ class WearEnhancedConfirmationViewModel : ViewModel() { val text = SpannableStringBuilder.valueOf(message) text.getSpans(0, message.length, URLSpan::class.java).map { val spanStart = text.getSpanStart(it) - val spanEnd = text.getSpanEnd(it) text.removeSpan(it) - text.delete(spanStart, spanEnd) + text.delete(spanStart, message.length) } return text } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearLocationProviderInterceptDialogViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearLocationProviderInterceptDialogViewModel.kt new file mode 100644 index 000000000..009ff952c --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearLocationProviderInterceptDialogViewModel.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.wear.model + +import android.content.Context +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.provider.Settings +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.utils.Utils + +class WearLocationProviderInterceptDialogViewModel : ViewModel() { + private val showDialogLiveData = MutableLiveData<Boolean>() + val dialogVisibilityLiveData: LiveData<Boolean> = showDialogLiveData + var locationProviderInterceptDialogArgs: LocationProviderInterceptDialogArgs? = null + + init { + showDialogLiveData.value = false + } + + private fun applicationInfo(context: Context, packageName: String): ApplicationInfo? { + val packageInfo: PackageInfo? = + try { + context.packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) + } catch (e: PackageManager.NameNotFoundException) { + null + } + return packageInfo?.applicationInfo + } + + fun showDialog(context: Context, packageName: String) { + val applicationInfo = applicationInfo(context, packageName) ?: return + val appLabel = Utils.getAppLabel(applicationInfo, context) + locationProviderInterceptDialogArgs = + LocationProviderInterceptDialogArgs( + iconId = R.drawable.ic_dialog_alert_material, + titleId = android.R.string.dialog_alert_title, + message = context.getString(R.string.location_warning, appLabel), + okButtonTitleId = R.string.ok, + locationSettingsId = R.string.location_settings, + onOkButtonClick = { dismissDialog() }, + onLocationSettingsClick = { + context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) + } + ) + showDialogLiveData.value = true + } + + fun dismissDialog() { + locationProviderInterceptDialogArgs = null + showDialogLiveData.value = false + } +} + +/** Factory for an AppPermissionGroupsRevokeDialogViewModel */ +class WearLocationProviderInterceptDialogViewModelFactory : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + @Suppress("UNCHECKED_CAST") return WearLocationProviderInterceptDialogViewModel() as T + } +} + +data class LocationProviderInterceptDialogArgs( + val iconId: Int, + val titleId: Int, + val message: String, + val okButtonTitleId: Int, + val locationSettingsId: Int, + val onOkButtonClick: () -> Unit, + val onLocationSettingsClick: () -> Unit +) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt deleted file mode 100644 index 380c3cfee..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.ui.wear.model - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel -import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState - -class WearPermissionUsageViewModel( - permissionUsagesUiState: PermissionUsagesUiState?, - showSystemApps: Boolean, - show7DaysData: Boolean -) : ViewModel() { - val permissionUsagesUiStateLiveData = MutableLiveData(permissionUsagesUiState) - /** A livedata which stores [BasePermissionUsageViewModel.getShowSystemApps()]. */ - val showSystemAppsLiveData = MutableLiveData(showSystemApps) - - /** A livedata which stores [BasePermissionUsageViewModel.getShow7DaysData()]. */ - val show7DaysLiveData = MutableLiveData(show7DaysData) - - fun updatePermissionUsagesUiStateLiveData(newUiData: PermissionUsagesUiState?) { - permissionUsagesUiStateLiveData.value = newUiData - } -} - -/** Factory for a WearPermissionsUsageViewModel */ -class WearPermissionUsageViewModelFactory(val viewModel: PermissionUsageViewModel) : - ViewModelProvider.Factory { - override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") - return WearPermissionUsageViewModel( - viewModel.permissionUsagesUiLiveData.value, - viewModel.getShowSystemApps(), - viewModel.getShow7DaysData() - ) - as T - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt new file mode 100644 index 000000000..c7ed0958c --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/ResourceHelper.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.content.Context +import android.os.SystemProperties +import androidx.annotation.ColorRes +import androidx.annotation.DimenRes +import androidx.annotation.DoNotInline +import androidx.annotation.StringRes +import androidx.compose.ui.graphics.Color + +internal object ResourceHelper { + + private const val MATERIAL3_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled" + + val material3Enabled: Boolean + get() { + return SystemProperties.getBoolean(MATERIAL3_ENABLED_SYSPROP, false) + } + + @DoNotInline + fun getColor(context: Context, @ColorRes id: Int): Color? { + return try { + val colorInt = context.resources.getColor(id, context.theme) + Color(colorInt) + } catch (e: Exception) { + null + } + } + + @DoNotInline + fun getString(context: Context, @StringRes id: Int): String? { + return try { + context.resources.getString(id) + } catch (e: Exception) { + null + } + } + + @DoNotInline + fun getDimen(context: Context, @DimenRes id: Int): Float? { + return try { + context.resources.getDimension(id) / context.resources.displayMetrics.density + } catch (e: Exception) { + null + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt new file mode 100644 index 000000000..7ac6c8114 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3ColorScheme.kt @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.content.Context +import android.os.Build +import androidx.annotation.ColorRes +import androidx.annotation.RequiresApi +import androidx.compose.ui.graphics.Color +import androidx.wear.compose.material3.ColorScheme + +/** + * Creates a dynamic color maps that can be overlaid. In wear we only support dark theme for the + * time being. If the device supports dynamic color generation these resources are updated with the + * generated colors + */ +internal object WearComposeMaterial3ColorScheme { + + @RequiresApi(Build.VERSION_CODES.S) + fun tonalColorScheme(context: Context): ColorScheme { + val tonalPalette = dynamicTonalPalette(context) + return ColorScheme( + background = tonalPalette.neutral0, + onBackground = tonalPalette.neutral100, + onPrimary = tonalPalette.primary10, + onPrimaryContainer = tonalPalette.primary90, + onSecondary = tonalPalette.secondary10, + onSecondaryContainer = tonalPalette.secondary90, + onSurface = tonalPalette.neutral95, + onSurfaceVariant = tonalPalette.neutralVariant80, + onTertiary = tonalPalette.tertiary10, + onTertiaryContainer = tonalPalette.tertiary90, + outline = tonalPalette.neutralVariant60, + outlineVariant = tonalPalette.neutralVariant40, + primary = tonalPalette.primary90, + primaryContainer = tonalPalette.primary30, + primaryDim = tonalPalette.primary80, + secondary = tonalPalette.secondary90, + secondaryContainer = tonalPalette.secondary30, + secondaryDim = tonalPalette.secondary80, + surfaceContainer = tonalPalette.neutral20, + surfaceContainerHigh = tonalPalette.neutral30, + tertiary = tonalPalette.tertiary90, + tertiaryContainer = tonalPalette.tertiary30, + tertiaryDim = tonalPalette.tertiary80, + ) + } + + private fun Color.updatedColor(context: Context, @ColorRes colorRes: Int): Color { + return ResourceHelper.getColor(context, colorRes) ?: this + } + + @RequiresApi(36) + fun dynamicColorScheme(context: Context): ColorScheme { + val defaultColorScheme = ColorScheme() + return ColorScheme( + primary = + defaultColorScheme.primary.updatedColor( + context, + android.R.color.system_primary_fixed, + ), + primaryDim = + defaultColorScheme.primaryDim.updatedColor( + context, + android.R.color.system_primary_fixed_dim, + ), + primaryContainer = + defaultColorScheme.primaryContainer.updatedColor( + context, + android.R.color.system_primary_container_dark, + ), + onPrimary = + defaultColorScheme.onPrimary.updatedColor( + context, + android.R.color.system_on_primary_fixed, + ), + onPrimaryContainer = + defaultColorScheme.onPrimaryContainer.updatedColor( + context, + android.R.color.system_on_primary_container_dark, + ), + secondary = + defaultColorScheme.secondary.updatedColor( + context, + android.R.color.system_secondary_fixed, + ), + secondaryDim = + defaultColorScheme.secondaryDim.updatedColor( + context, + android.R.color.system_secondary_fixed_dim, + ), + secondaryContainer = + defaultColorScheme.secondaryContainer.updatedColor( + context, + android.R.color.system_secondary_container_dark, + ), + onSecondary = + defaultColorScheme.onSecondary.updatedColor( + context, + android.R.color.system_on_secondary_fixed, + ), + onSecondaryContainer = + defaultColorScheme.onSecondaryContainer.updatedColor( + context, + android.R.color.system_on_secondary_container_dark, + ), + tertiary = + defaultColorScheme.tertiary.updatedColor( + context, + android.R.color.system_tertiary_fixed, + ), + tertiaryDim = + defaultColorScheme.tertiaryDim.updatedColor( + context, + android.R.color.system_tertiary_fixed_dim, + ), + tertiaryContainer = + defaultColorScheme.tertiaryContainer.updatedColor( + context, + android.R.color.system_tertiary_container_dark, + ), + onTertiary = + defaultColorScheme.onTertiary.updatedColor( + context, + android.R.color.system_on_tertiary_fixed, + ), + onTertiaryContainer = + defaultColorScheme.onTertiaryContainer.updatedColor( + context, + android.R.color.system_on_tertiary_container_dark, + ), + surfaceContainerLow = + defaultColorScheme.surfaceContainerLow.updatedColor( + context, + android.R.color.system_surface_container_low_dark, + ), + surfaceContainer = + defaultColorScheme.surfaceContainer.updatedColor( + context, + android.R.color.system_surface_container_dark, + ), + surfaceContainerHigh = + defaultColorScheme.surfaceContainerHigh.updatedColor( + context, + android.R.color.system_surface_container_high_dark, + ), + onSurface = + defaultColorScheme.onSurface.updatedColor( + context, + android.R.color.system_on_surface_dark, + ), + onSurfaceVariant = + defaultColorScheme.onSurfaceVariant.updatedColor( + context, + android.R.color.system_on_surface_variant_dark, + ), + outline = + defaultColorScheme.outline.updatedColor( + context, + android.R.color.system_outline_dark, + ), + outlineVariant = + defaultColorScheme.outlineVariant.updatedColor( + context, + android.R.color.system_outline_variant_dark, + ), + background = + defaultColorScheme.background.updatedColor( + context, + android.R.color.system_background_dark, + ), + onBackground = + defaultColorScheme.onBackground.updatedColor( + context, + android.R.color.system_on_background_dark, + ), + error = + defaultColorScheme.error.updatedColor(context, android.R.color.system_error_dark), + onError = + defaultColorScheme.onError.updatedColor( + context, + android.R.color.system_on_error_dark, + ), + errorContainer = + defaultColorScheme.errorContainer.updatedColor( + context, + android.R.color.system_error_container_dark, + ), + onErrorContainer = + defaultColorScheme.onErrorContainer.updatedColor( + context, + android.R.color.system_on_error_container_dark, + ), + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt new file mode 100644 index 000000000..f81022842 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Shapes.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.content.Context +import androidx.annotation.DimenRes +import androidx.compose.foundation.shape.CornerBasedShape +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material3.Shapes +import com.android.permissioncontroller.R + +// TODO(b/324928718): Use system defined symbols. +internal object WearComposeMaterial3Shapes { + private fun CornerBasedShape.updatedShape( + context: Context, + @DimenRes cornerSizeRes: Int, + ): CornerBasedShape { + val size = ResourceHelper.getDimen(context, cornerSizeRes)?.dp ?: return this + return copy(CornerSize(size)) + } + + fun dynamicShapes(context: Context): Shapes { + val defaultShapes = Shapes() + return Shapes( + extraLarge = + defaultShapes.extraLarge.updatedShape( + context, + R.dimen.wear_compose_material3_shape_corner_extra_large_size, + ), + large = + defaultShapes.large.updatedShape( + context, + R.dimen.wear_compose_material3_shape_corner_large_size, + ), + medium = + defaultShapes.medium.updatedShape( + context, + R.dimen.wear_compose_material3_shape_corner_medium_size, + ), + small = + defaultShapes.small.updatedShape( + context, + R.dimen.wear_compose_material3_shape_corner_small_size, + ), + extraSmall = + defaultShapes.extraSmall.updatedShape( + context, + R.dimen.wear_compose_material3_shape_corner_extra_small_size, + ), + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt new file mode 100644 index 000000000..a4ec9ee1d --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3TypeScaleTokens.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +/* + * These values are retrieved from https://carbon.googleplex.com/wear-m3/pages and + * modified by UX. + * These values are internal to material3 library. We copied them to support flex font families + * Do not edit directly. Copy paste from compose material library. + */ + +internal object WearComposeMaterial3TypeScaleTokens { + val ArcLargeRoundness = 100.0f + val ArcLargeWeight = 600.0f + val ArcLargeWidth = 100.0f + + val ArcMediumRoundness = 100.0f + val ArcMediumWeight = 600.0f + val ArcMediumWidth = 90.0f + + val ArcSmallRoundness = 100.0f + val ArcSmallWeight = 550.0f + val ArcSmallWidth = 90.0f + + val BodyExtraSmallRoundness = 100.0f + val BodyExtraSmallWeight = 500.0f + val BodyExtraSmallWidth = 84.0f + + val BodyLargeRoundness = 100.0f + val BodyLargeWeight = 450.0f + val BodyLargeWidth = 90.0f + + val BodyMediumRoundness = 100.0f + val BodyMediumWeight = 450.0f + val BodyMediumWidth = 90.0f + + val BodySmallRoundness = 100.0f + val BodySmallWeight = 500.0f + val BodySmallWidth = 86.0f + + val DisplayLargeRoundness = 100.0f + val DisplayLargeWeight = 450.0f + val DisplayLargeWidth = 100.0f + + val DisplayMediumRoundness = 100.0f + val DisplayMediumWeight = 500.0f + val DisplayMediumWidth = 100.0f + + val DisplaySmallRoundness = 100.0f + val DisplaySmallWeight = 500.0f + val DisplaySmallWidth = 100.0f + + val LabelLargeRoundness = 100.0f + val LabelLargeWeight = 500.0f + val LabelLargeWidth = 100.0f + + val LabelMediumRoundness = 100.0f + val LabelMediumWeight = 500.0f + val LabelMediumWidth = 90.0f + + val LabelSmallRoundness = 100.0f + val LabelSmallWeight = 500.0f + val LabelSmallWidth = 84.0f + + val NumeralExtraLargeRoundness = 100.0f + val NumeralExtraLargeWeight = 550.0f + val NumeralExtraLargeWidth = 100.0f + + val NumeralExtraSmallRoundness = 100.0f + val NumeralExtraSmallWeight = 550.0f + val NumeralExtraSmallWidth = 100.0f + + val NumeralLargeRoundness = 100.0f + val NumeralLargeWeight = 600.0f + val NumeralLargeWidth = 100.0f + + val NumeralMediumRoundness = 100.0f + val NumeralMediumWidth = 100.0f + val NumeralMediumWeight = 600.0f + + val NumeralSmallRoundness = 100.0f + val NumeralSmallWeight = 600.0f + val NumeralSmallWidth = 100.0f + + val TitleLargeRoundness = 100.0f + val TitleLargeWeight = 500.0f + val TitleLargeWidth = 100.0f + + val TitleMediumRoundness = 100.0f + val TitleMediumWeight = 550.0f + val TitleMediumWidth = 100.0f + + val TitleSmallRoundness = 100.0f + val TitleSmallWeight = 550.0f + val TitleSmallWidth = 100.0f +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt new file mode 100644 index 000000000..ceae526a7 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3Typography.kt @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.content.Context +import androidx.annotation.DimenRes +import androidx.annotation.StringRes +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.DeviceFontFamilyName +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontVariation +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material3.Typography +import com.android.permissioncontroller.R + +internal object WearComposeMaterial3Typography { + + private const val DEVICE_DEFAULT_FLEX_FONT_TYPE = "font-family-flex-device-default" + + fun fontFamily( + context: Context, + @StringRes id: Int, + variationSettings: FontVariation.Settings? = null, + ): FontFamily { + val typefaceName = ResourceHelper.getString(context, id) ?: DEVICE_DEFAULT_FLEX_FONT_TYPE + + val font = + if (variationSettings != null) { + Font( + familyName = DeviceFontFamilyName(typefaceName), + variationSettings = variationSettings, + ) + } else { + Font(familyName = DeviceFontFamilyName(typefaceName)) + } + return FontFamily(font) + } + + private fun TextStyle.updatedTextStyle( + context: Context, + @StringRes fontRes: Int, + variationSettings: FontVariation.Settings? = null, + @DimenRes fontSizeRes: Int, + ): TextStyle { + + val fontFamily = + fontFamily(context = context, id = fontRes, variationSettings = variationSettings) + val fontSize = ResourceHelper.getDimen(context = context, id = fontSizeRes)?.sp ?: fontSize + + return copy(fontFamily = fontFamily, fontSize = fontSize) + } + + fun dynamicTypography(context: Context): Typography { + val defaultTypography = Typography() + return Typography( + arcLarge = + defaultTypography.arcLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_arc_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_arc_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.ArcLargeVariationSettings, + ), + arcMedium = + defaultTypography.arcMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_arc_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_arc_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.ArcMediumVariationSettings, + ), + arcSmall = + defaultTypography.arcSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_arc_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_arc_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.ArcSmallVariationSettings, + ), + bodyLarge = + defaultTypography.bodyLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_body_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_body_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.BodyLargeVariationSettings, + ), + bodyMedium = + defaultTypography.bodyMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_body_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_body_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.BodyMediumVariationSettings, + ), + bodySmall = + defaultTypography.bodySmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_body_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_body_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.BodySmallVariationSettings, + ), + bodyExtraSmall = + defaultTypography.bodyExtraSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_body_extra_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_body_extra_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.BodyExtraSmallVariationSettings, + ), + displayLarge = + defaultTypography.displayLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_display_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_display_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.DisplayLargeVariationSettings, + ), + displayMedium = + defaultTypography.displayMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_display_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_display_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.DisplayMediumVariationSettings, + ), + displaySmall = + defaultTypography.displaySmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_display_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_display_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.DisplaySmallVariationSettings, + ), + labelLarge = + defaultTypography.labelLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_label_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_label_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.LabelLargeVariationSettings, + ), + labelMedium = + defaultTypography.labelMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_label_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_label_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.LabelMediumVariationSettings, + ), + labelSmall = + defaultTypography.labelSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_label_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_label_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.LabelSmallVariationSettings, + ), + numeralExtraLarge = + defaultTypography.numeralExtraLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_numeral_extra_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_numeral_extra_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.NumeralExtraLargeVariationSettings, + ), + numeralLarge = + defaultTypography.numeralLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_numeral_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_numeral_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.NumeralLargeVariationSettings, + ), + numeralMedium = + defaultTypography.numeralMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_numeral_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_numeral_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.NumeralMediumVariationSettings, + ), + numeralSmall = + defaultTypography.numeralSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_numeral_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_numeral_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.NumeralSmallVariationSettings, + ), + numeralExtraSmall = + defaultTypography.numeralExtraSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_numeral_extra_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_numeral_extra_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.NumeralExtraSmallVariationSettings, + ), + titleLarge = + defaultTypography.titleLarge.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_title_large_font_family, + fontSizeRes = R.dimen.wear_compose_material3_title_large_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.TitleLargeVariationSettings, + ), + titleMedium = + defaultTypography.titleMedium.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_title_medium_font_family, + fontSizeRes = R.dimen.wear_compose_material3_title_medium_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.TitleMediumVariationSettings, + ), + titleSmall = + defaultTypography.titleSmall.updatedTextStyle( + context = context, + fontRes = R.string.wear_compose_material3_title_small_font_family, + fontSizeRes = R.dimen.wear_compose_material3_title_small_font_size, + variationSettings = + WearComposeMaterial3VariableFontTokens.TitleSmallVariationSettings, + ), + ) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt new file mode 100644 index 000000000..1b42a3b05 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearComposeMaterial3VariableFontTokens.kt @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import androidx.compose.ui.text.font.FontVariation + +internal object WearComposeMaterial3VariableFontTokens { + val ArcLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcLargeRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcLargeWeight), + ) + val ArcMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcMediumRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcMediumWeight), + ) + val ArcSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.ArcSmallRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.ArcSmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.ArcSmallWeight), + ) + val BodyExtraSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.BodyExtraSmallRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyExtraSmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyExtraSmallWeight), + ) + val BodyLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodyLargeRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyLargeWeight), + ) + val BodyMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodyMediumRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodyMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodyMediumWeight), + ) + val BodySmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.BodySmallRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.BodySmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.BodySmallWeight), + ) + val DisplayLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.DisplayLargeRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplayLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplayLargeWeight), + ) + val DisplayMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.DisplayMediumRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplayMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplayMediumWeight), + ) + val DisplaySmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.DisplaySmallRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.DisplaySmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.DisplaySmallWeight), + ) + val LabelLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelLargeRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelLargeWeight), + ) + val LabelMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelMediumRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelMediumWeight), + ) + val LabelSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.LabelSmallRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.LabelSmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.LabelSmallWeight), + ) + val NumeralExtraLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeRoundness, + ), + FontVariation.Setting( + "wdth", + WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeWidth, + ), + FontVariation.Setting( + "wght", + WearComposeMaterial3TypeScaleTokens.NumeralExtraLargeWeight, + ), + ) + val NumeralExtraSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallRoundness, + ), + FontVariation.Setting( + "wdth", + WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallWidth, + ), + FontVariation.Setting( + "wght", + WearComposeMaterial3TypeScaleTokens.NumeralExtraSmallWeight, + ), + ) + val NumeralLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.NumeralLargeRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralLargeWeight), + ) + val NumeralMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.NumeralMediumRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralMediumWeight), + ) + val NumeralSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting( + "ROND", + WearComposeMaterial3TypeScaleTokens.NumeralSmallRoundness, + ), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.NumeralSmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.NumeralSmallWeight), + ) + val TitleLargeVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleLargeRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleLargeWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleLargeWeight), + ) + val TitleMediumVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleMediumRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleMediumWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleMediumWeight), + ) + val TitleSmallVariationSettings = + FontVariation.Settings( + FontVariation.Setting("ROND", WearComposeMaterial3TypeScaleTokens.TitleSmallRoundness), + FontVariation.Setting("wdth", WearComposeMaterial3TypeScaleTokens.TitleSmallWidth), + FontVariation.Setting("wght", WearComposeMaterial3TypeScaleTokens.TitleSmallWeight), + ) +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt new file mode 100644 index 000000000..160dc2e93 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearMaterialBridgedLegacyTheme.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material.Colors +import androidx.wear.compose.material.Shapes +import androidx.wear.compose.material.Typography + +/** + * This exists to support Permission Controller screens that may still use Material 2.5 components + * to maintain consistency with the settings screens. + * + * However to avoid maintaining two sets of resources for overlays, this class construct 2.5 theme + * from 3.0 + */ +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +internal class WearMaterialBridgedLegacyTheme +private constructor(newTheme: WearOverlayableMaterial3Theme) { + + val colors = + newTheme.colorScheme.run { + Colors( + background = background, + onBackground = onBackground, + primary = onPrimaryContainer, // primary90 + primaryVariant = primaryDim, // primary80 + onPrimary = onPrimary, // primary10 + secondary = tertiary, // Tertiary90 + secondaryVariant = tertiaryDim, // Tertiary60 - Tertiary80 BestFit. + onSecondary = onTertiary, // Tertiary10 + surface = surfaceContainer, // neutral20 + onSurface = onSurface, // neutral95 + onSurfaceVariant = onSurfaceVariant, // neutralVariant80 + ) + } + + // Based on: + // Material 2: + // wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Typography.kt + // Material 3: + // wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt + val typography = + newTheme.typography.run { + Typography( + display1 = displayLarge, // 40.sp + display2 = displayMedium.copy(fontSize = 34.sp, lineHeight = 40.sp), + display3 = displayMedium, // 30.sp + title1 = displaySmall, // 24.sp + title2 = titleLarge, // 20.sp + title3 = titleMedium, // 16.sp + body1 = bodyLarge, // 16.sp + body2 = bodyMedium, // 14.sp + caption1 = bodyMedium, // 14.sp + caption2 = bodySmall, // 12.sp + caption3 = bodyExtraSmall, // 10.sp + button = labelMedium, // 15.sp + ) + } + + val shapes = newTheme.shapes.run { Shapes(large = large, medium = medium, small = small) } + + companion object { + fun createFrom(newTheme: WearOverlayableMaterial3Theme) = + WearMaterialBridgedLegacyTheme(newTheme) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt new file mode 100644 index 000000000..8aeb5f74d --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearOverlayableMaterial3Theme.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.permissioncontroller.permission.ui.wear.theme + +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi + +/** + * Theme wrapper providing Material 3 styling while maintaining compatibility with Runtime Resource + * Overlay (RRO). + * + * Uses the tonal palette from the previous Material Design version until dynamic color tokens are + * available in SDK 36. + */ +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +internal class WearOverlayableMaterial3Theme(context: Context) { + val colorScheme = + if (Build.VERSION.SDK_INT >= 36) { + WearComposeMaterial3ColorScheme.dynamicColorScheme(context) + } else { + WearComposeMaterial3ColorScheme.tonalColorScheme(context) + } + + val typography = WearComposeMaterial3Typography.dynamicTypography(context) + + val shapes = WearComposeMaterial3Shapes.dynamicShapes(context) +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt index 933cf19f9..8823bee07 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.permissioncontroller.permission.ui.wear.theme import android.content.Context @@ -14,11 +29,71 @@ import androidx.compose.ui.text.font.FontFamily import androidx.wear.compose.material.Colors import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.Typography +import androidx.wear.compose.material3.MaterialTheme as Material3Theme import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3 + +/** This enum is used to specify the material version used for a specific screen */ +enum class WearPermissionMaterialUIVersion { + MATERIAL2_5, + MATERIAL3, +} + +/** + * Supports both Material 3 and Material 2_5 theme. default version for permission theme will be 2_5 + * until we migrate enough screens to 3. 2_5 version will use material 3 overlay resources if we + * enable material3 for even one screen (Permission screens will be migrated in phases). + */ +@Composable +fun WearPermissionTheme( + version: WearPermissionMaterialUIVersion = MATERIAL2_5, + content: @Composable () -> Unit, +) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + WearPermissionLegacyTheme(content) + } else { + // Whether we are ready to use material3 for any screen. + val useBridgedTheme = ResourceHelper.material3Enabled -/** The Material 3 Theme Wrapper for Supporting RRO. */ + // Material3 UI controls are still being used in the screen that the theme is applied + if (version == MATERIAL3) { + val material3Theme = WearOverlayableMaterial3Theme(LocalContext.current) + Material3Theme( + colorScheme = material3Theme.colorScheme, + typography = material3Theme.typography, + shapes = material3Theme.shapes, + content = content, + ) + } + // Material2_5 UI controls are still being used in the screen that the theme is applied, + // But some in-app screens(like permission grant screen) are migrated to material3. + // To avoid having two set of overlay resources, we will use material3 overlay resources to + // support material2_5 UI controls as well. + else if (version == MATERIAL2_5 && useBridgedTheme) { + val material3Theme = WearOverlayableMaterial3Theme(LocalContext.current) + val bridgedLegacyTheme = WearMaterialBridgedLegacyTheme.createFrom(material3Theme) + MaterialTheme( + colors = bridgedLegacyTheme.colors, + typography = bridgedLegacyTheme.typography, + shapes = bridgedLegacyTheme.shapes, + content = content, + ) + } + // We are not ready for material3 yet in any screens. + else { + WearPermissionLegacyTheme(content) + } + } +} + +/** + * The Material 2.5 Theme Wrapper for Supporting RRO with legacy resources. This theme is kept here + * for backward compatibility. When grant screen is updated to material3 will clean up legacy + * resources. + */ @Composable -fun WearPermissionTheme(content: @Composable () -> Unit) { +fun WearPermissionLegacyTheme(content: @Composable () -> Unit) { val context = LocalContext.current val colors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt index b822aa541..fb33aaffc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt @@ -143,9 +143,6 @@ object KotlinUtils { /** Whether to show the location indicators. */ private const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_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 the photo picker option in permission prompts. */ private const val PROPERTY_PHOTO_PICKER_PROMPT_ENABLED = "photo_picker_prompt_enabled" @@ -164,10 +161,6 @@ object KotlinUtils { private const val PROPERTY_SAFETY_LABEL_CHANGES_JOB_RUN_WHEN_IDLE = "safety_label_changes_job_run_when_idle" - /** Whether the kill switch is set for [SafetyLabelChangesJobService]. */ - private const val PROPERTY_SAFETY_LABEL_CHANGES_JOB_SERVICE_KILL_SWITCH = - "safety_label_changes_job_service_kill_switch" - data class Quadruple<out A, out B, out C, out D>( val first: A, val second: B, @@ -208,21 +201,6 @@ object KotlinUtils { } /** - * 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. @@ -278,20 +256,6 @@ object KotlinUtils { !DeviceUtils.isWear(context) } - /** - * Whether the kill switch is set for [SafetyLabelChangesJobService]. If {@code true}, the - * service is effectively disabled and will not run or schedule any jobs. - */ - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") - fun safetyLabelChangesJobServiceKillSwitch(): Boolean { - return SdkLevel.isAtLeastU() && - DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_LABEL_CHANGES_JOB_SERVICE_KILL_SWITCH, - false - ) - } - /** How often the safety label changes job will run. */ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") fun getSafetyLabelChangesJobIntervalMillis(): Long { @@ -302,16 +266,6 @@ object KotlinUtils { ) } - /** Whether the safety label changes job should only be run when the device is idle. */ - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") - fun runSafetyLabelChangesJobOnlyWhenDeviceIdle(): Boolean { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_LABEL_CHANGES_JOB_RUN_WHEN_IDLE, - true - ) - } - /** * 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 which @@ -771,7 +725,7 @@ object KotlinUtils { LightPermission( group.packageInfo, perm.permInfo, - perm.isGrantedIncludingAppOp, + perm.isGranted, perm.flags or flagsToSet, perm.foregroundPerms ) @@ -904,7 +858,7 @@ object KotlinUtils { group.specialLocationGrant ) // If any permission in the group is one time granted, start one time permission session. - if (newGroup.permissions.any { it.value.isOneTime && it.value.isGrantedIncludingAppOp }) { + if (newGroup.permissions.any { it.value.isOneTime && it.value.isGranted }) { if (SdkLevel.isAtLeastT()) { context .getSystemService(PermissionManager::class.java)!! @@ -966,7 +920,7 @@ object KotlinUtils { var newFlags = perm.flags var oldFlags = perm.flags - var isGranted = perm.isGrantedIncludingAppOp + var isGranted = perm.isGranted var shouldKill = false // Create a new context with the given deviceId so that permission updates will be bound @@ -974,7 +928,7 @@ object KotlinUtils { val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId) // Grant the permission if needed. - if (!perm.isGrantedIncludingAppOp) { + if (!perm.isGranted) { val affectsAppOp = permissionToOp(perm.name) != null || perm.isBackgroundPermission // TODO 195016052: investigate adding split permission handling @@ -1043,14 +997,14 @@ object KotlinUtils { // If we newly grant background access to the fine location, double-guess the user some // time later if this was really the right choice. - if (!perm.isGrantedIncludingAppOp && isGranted) { + if (!perm.isGranted && isGranted) { var triggerLocationAccessCheck = false if (perm.name == ACCESS_FINE_LOCATION) { val bgPerm = group.permissions[perm.backgroundPermission] - triggerLocationAccessCheck = bgPerm?.isGrantedIncludingAppOp == true + triggerLocationAccessCheck = bgPerm?.isGranted == true } else if (perm.name == ACCESS_BACKGROUND_LOCATION) { val fgPerm = group.permissions[ACCESS_FINE_LOCATION] - triggerLocationAccessCheck = fgPerm?.isGrantedIncludingAppOp == true + triggerLocationAccessCheck = fgPerm?.isGranted == true } if (triggerLocationAccessCheck) { // trigger location access check @@ -1289,7 +1243,7 @@ object KotlinUtils { val user = UserHandle.getUserHandleForUid(group.packageInfo.uid) var newFlags = perm.flags val deviceId = group.deviceId - var isGranted = perm.isGrantedIncludingAppOp + var isGranted = perm.isGranted val supportsRuntime = group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.M var shouldKill = false @@ -1299,7 +1253,7 @@ object KotlinUtils { // to the device val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId) - if (perm.isGrantedIncludingAppOp || (perm.isCompatRevoked && forceRemoveRevokedCompat)) { + if (perm.isGranted || (perm.isCompatRevoked && forceRemoveRevokedCompat)) { if ( supportsRuntime && !isPermissionSplitFromNonRuntime( @@ -1363,14 +1317,14 @@ object KotlinUtils { // If we revoke background access to the fine location, we trigger a check to remove // notification warning about background location access - if (perm.isGrantedIncludingAppOp && !isGranted) { + if (perm.isGranted && !isGranted) { var cancelLocationAccessWarning = false if (perm.name == ACCESS_FINE_LOCATION) { val bgPerm = group.permissions[perm.backgroundPermission] - cancelLocationAccessWarning = bgPerm?.isGrantedIncludingAppOp == true + cancelLocationAccessWarning = bgPerm?.isGranted == true } else if (perm.name == ACCESS_BACKGROUND_LOCATION) { val fgPerm = group.permissions[ACCESS_FINE_LOCATION] - cancelLocationAccessWarning = fgPerm?.isGrantedIncludingAppOp == true + cancelLocationAccessWarning = fgPerm?.isGranted == true } if (cancelLocationAccessWarning) { // cancel location access warning notification @@ -1430,7 +1384,7 @@ object KotlinUtils { val fgPerm = group.permissions[foregroundPermName] val appOpName = permissionToOp(foregroundPermName) ?: continue - if (fgPerm != null && fgPerm.isGrantedIncludingAppOp) { + if (fgPerm != null && fgPerm.isGranted) { wasChanged = setOpMode(appOpName, uid, packageName, MODE_ALLOWED, appOpsManager) || wasChanged @@ -1443,7 +1397,7 @@ object KotlinUtils { if (group.permissions.containsKey(perm.backgroundPermission)) { val bgPerm = group.permissions[perm.backgroundPermission] val mode = - if (bgPerm != null && bgPerm.isGrantedIncludingAppOp) MODE_ALLOWED + if (bgPerm != null && bgPerm.isGranted) MODE_ALLOWED else MODE_FOREGROUND setOpMode(appOpName, uid, packageName, mode, appOpsManager) @@ -1490,7 +1444,7 @@ object KotlinUtils { if (perm.isBackgroundPermission && perm.foregroundPerms != null) { for (foregroundPermName in perm.foregroundPerms) { val fgPerm = group.permissions[foregroundPermName] - if (fgPerm != null && fgPerm.isGrantedIncludingAppOp) { + if (fgPerm != null && fgPerm.isGranted) { val appOpName = permissionToOp(foregroundPermName) ?: return false wasChanged = wasChanged || diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt index f97acb130..a3446f802 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt @@ -22,6 +22,7 @@ import android.app.AppOpsManager import android.content.pm.PackageManager import android.content.pm.PermissionInfo import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP +import android.permission.flags.Flags import android.util.Log import com.android.modules.utils.build.SdkLevel import com.android.permission.safetylabel.DataCategoryConstants @@ -138,6 +139,12 @@ object PermissionMapping { PLATFORM_PERMISSIONS[Manifest.permission.NEARBY_WIFI_DEVICES] = Manifest.permission_group.NEARBY_DEVICES } + // Ranging permission will be supported from Android B+, update this when isAtLeastB() + // is available. + if (SdkLevel.isAtLeastV() && Flags.rangingPermissionEnabled()) { + PLATFORM_PERMISSIONS[Manifest.permission.RANGING] = + 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 @@ -374,6 +381,13 @@ object PermissionMapping { if (opName == AppOpsManager.OPSTR_PHONE_CALL_CAMERA) { return Manifest.permission_group.CAMERA } + if ( + SdkLevel.isAtLeastV() && + Flags.locationBypassPrivacyDashboardEnabled() && + opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION + ) { + return Manifest.permission_group.LOCATION + } return try { AppOpsManager.opToPermission(opName)?.let { getGroupOfPlatformPermission(it) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/ResourceUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/ResourceUtils.kt new file mode 100644 index 000000000..efe3361b1 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/ResourceUtils.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.utils +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.AttrRes +import androidx.core.content.res.use + +object ResourceUtils { + fun getResourceIdByAttr(context: Context, attrs: AttributeSet?, @AttrRes attr: Int) = + context.obtainStyledAttributes(attrs, intArrayOf(attr)).use { it.getResourceId(0, 0) } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyNetLogger.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyNetLogger.java index 828857cc6..c9b023c44 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyNetLogger.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyNetLogger.java @@ -95,7 +95,7 @@ public final class SafetyNetLogger { } builder.append(permission.getName()).append('|'); - builder.append(permission.isGrantedIncludingAppOp()).append('|'); + builder.append(permission.isGranted()).append('|'); builder.append(permission.getFlags()); } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java index e5de63f32..3d3b47272 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java @@ -126,6 +126,7 @@ import java.util.List; import java.util.Locale; import java.util.Random; import java.util.Set; +import java.util.function.Supplier; public final class Utils { @@ -1566,18 +1567,40 @@ public final class Utils { public static String getEnterpriseString(@NonNull Context context, @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) { return SdkLevel.isAtLeastT() - ? getUpdatableEnterpriseString( - context, updatableStringId, defaultStringId, formatArgs) + ? getUpdatableEnterpriseString(context, updatableStringId, + () -> context.getString(defaultStringId, formatArgs), formatArgs) : context.getString(defaultStringId, formatArgs); } + /** + * Selects the appropriate enterprise string for the provided resource ID and a fallback string + */ + @NonNull + public static String getEnterpriseString(@NonNull Context context, + @NonNull String updatableStringId, @NonNull String defaultString) { + return SdkLevel.isAtLeastT() + ? getUpdatableEnterpriseString(context, updatableStringId, () -> defaultString) + : defaultString; + } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull private static String getUpdatableEnterpriseString(@NonNull Context context, - @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) { + @NonNull String updatableStringId, @NonNull Supplier<String> defaultStringLoader, + @NonNull Object... formatArgs) { DevicePolicyManager dpm = getSystemServiceSafe(context, DevicePolicyManager.class); - return dpm.getResources().getString(updatableStringId, () -> context.getString( - defaultStringId, formatArgs), formatArgs); + return dpm.getResources().getString(updatableStringId, defaultStringLoader, formatArgs); + } + + /** + * Returns the profile label from the {@link UserManager} for the provided profile + */ + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + @NonNull + public static String getProfileLabel(@NonNull UserHandle profile, @NonNull Context context) { + Context profileContext = context.createContextAsUser(profile, 0); + UserManager profileUserManager = profileContext.getSystemService(UserManager.class); + return profileUserManager.getProfileLabel(); } /** diff --git a/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt b/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt index faef36f68..9e667d971 100644 --- a/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt @@ -34,3 +34,12 @@ data class PackageInfoModel( requireNotNull(packageInfo.applicationInfo).flags ) } + +data class PackageAttributionModel( + val packageName: String, + val areUserVisible: Boolean = false, + /** A map of attribution tag to attribution resource label identifier. */ + val tagResourceMap: Map<String, Int>? = null, + /** A map of attribution resource label identifier to attribution label. */ + val resourceLabelMap: Map<Int, String>? = null, +) diff --git a/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt index 40c714f55..a7e06148a 100644 --- a/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt +++ b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt @@ -18,9 +18,13 @@ package com.android.permissioncontroller.pm.data.repository.v31 import android.app.Application import android.content.pm.PackageManager +import android.content.res.Resources +import android.graphics.drawable.Drawable import android.os.UserHandle import android.util.Log +import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.pm.data.model.v31.PackageAttributionModel import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel import kotlin.concurrent.Volatile import kotlinx.coroutines.CoroutineDispatcher @@ -32,12 +36,37 @@ import kotlinx.coroutines.withContext * shouldn't access [PackageManager] directly, instead they should use the repository. */ interface PackageRepository { + /** + * Returns a package label for the given [packageName] and [user] Returns [packageName] if the + * package is not found. + */ + fun getPackageLabel(packageName: String, user: UserHandle): String + + /** + * Returns a package's badged icon for the given [packageName] and [user] Returns null if the + * package is not found. + */ + fun getBadgedPackageIcon(packageName: String, user: UserHandle): Drawable? + + /** + * Returns a [PackageInfoModel] for the given [packageName] and [user] Returns null if the + * package is not found. + */ suspend fun getPackageInfo( packageName: String, user: UserHandle, flags: Int = PackageManager.GET_PERMISSIONS ): PackageInfoModel? + /** + * Returns a [PackageAttributionModel] for the given [packageName] and [user] Returns null if + * the package is not found. + */ + suspend fun getPackageAttributionInfo( + packageName: String, + user: UserHandle, + ): PackageAttributionModel? + companion object { @Volatile private var instance: PackageRepository? = null @@ -50,6 +79,26 @@ class PackageRepositoryImpl( private val app: Application, private val dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : PackageRepository { + override fun getPackageLabel(packageName: String, user: UserHandle): String { + return try { + val userContext = Utils.getUserContext(app, user) + val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0) + Utils.getFullAppLabel(appInfo, app) + } catch (e: PackageManager.NameNotFoundException) { + packageName + } + } + + override fun getBadgedPackageIcon(packageName: String, user: UserHandle): Drawable? { + return try { + val userContext = Utils.getUserContext(app, user) + val appInfo = userContext.packageManager.getApplicationInfo(packageName, 0) + Utils.getBadgedIcon(app, appInfo) + } catch (e: PackageManager.NameNotFoundException) { + null + } + } + override suspend fun getPackageInfo( packageName: String, user: UserHandle, @@ -60,7 +109,7 @@ class PackageRepositoryImpl( val packageInfo = Utils.getUserContext(app, user) .packageManager - .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) + .getPackageInfo(packageName, flags) PackageInfoModel(packageInfo) } catch (e: PackageManager.NameNotFoundException) { Log.w(LOG_TAG, "package $packageName not found for user ${user.identifier}") @@ -68,6 +117,50 @@ class PackageRepositoryImpl( } } + @Suppress("DEPRECATION") + override suspend fun getPackageAttributionInfo( + packageName: String, + user: UserHandle, + ): PackageAttributionModel? = + withContext(dispatcher) { + try { + val packageInfo = + Utils.getUserContext(app, user) + .packageManager + .getPackageInfo(packageName, PackageManager.GET_ATTRIBUTIONS) + val attributionUserVisible = + packageInfo.applicationInfo?.areAttributionsUserVisible() ?: false + if (attributionUserVisible && SdkLevel.isAtLeastS()) { + val pkgContext = app.createPackageContext(packageName, 0) + val attributionTagToLabelRes = + packageInfo.attributions?.associate { it.tag to it.label } + val labelResToLabelStringMap = + attributionTagToLabelRes + ?.mapNotNull { entry -> + val labelString = + try { + pkgContext.getString(entry.value) + } catch (e: Resources.NotFoundException) { + null + } + if (labelString != null) entry.value to labelString else null + } + ?.toMap() + PackageAttributionModel( + packageName, + true, + attributionTagToLabelRes, + labelResToLabelStringMap + ) + } else { + PackageAttributionModel(packageName) + } + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "package $packageName not found for user ${user.identifier}") + null + } + } + companion object { private const val LOG_TAG = "PackageRepository" } diff --git a/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/package-info.java b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/package-info.java new file mode 100644 index 000000000..5cfb75e31 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.S) +package com.android.permissioncontroller.pm.data.repository.v31; diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt index c633c013a..1610901bc 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt @@ -48,7 +48,6 @@ import androidx.annotation.GuardedBy import androidx.annotation.RequiresApi import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread -import androidx.core.util.Preconditions import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.Constants import com.android.permissioncontroller.PermissionControllerStatsLog @@ -712,7 +711,7 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() { return } - val data = Preconditions.checkNotNull(intent.data) + val data = requireNotNull(intent.data) val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) coroutineScope.launch(Dispatchers.Default) { if (DEBUG) { diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt index 43b3edc04..58a6f1bc4 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt @@ -57,7 +57,6 @@ import androidx.annotation.GuardedBy import androidx.annotation.RequiresApi import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread -import androidx.core.util.Preconditions import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.Constants import com.android.permissioncontroller.Constants.KEY_LAST_NOTIFICATION_LISTENER_NOTIFICATION_SHOWN @@ -1146,7 +1145,7 @@ class NotificationListenerPackageResetHandler : BroadcastReceiver() { return } - val data = Preconditions.checkNotNull(intent.data) + val data = requireNotNull(intent.data) val pkg: String = data.schemeSpecificPart if (DEBUG) Log.i(TAG, "Reset $pkg") diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING index 3e8c5a19c..abf0c4101 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING @@ -23,18 +23,7 @@ ] }, { - "name": "CtsPermissionTestCases", - "options": [ - { - "include-filter": "android.permission.cts.NotificationListenerCheckTest" - }, - { - "include-filter": "android.permission.cts.AccessibilityPrivacySourceTest" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] + "name": "CtsPermissionTestCases_PrivacySources" } ], "postsubmit": [ diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md index acdfffb50..255214495 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/Role.md +++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md @@ -31,8 +31,8 @@ The list of available roles and their behavior can be updated via PermissionCont of the platform release cycle. Since Android Q, all the default apps (e.g. default SMS app) are backed by a corresponding role implementation. -The definition for all the roles can be found in [roles.xml](../../../../../res/xml/roles.xml) and -associated [`RoleBehavior`](model/RoleBehavior.java) classes. +The definition for all the roles can be found in [roles.xml][roles-xml] and +associated [`RoleBehavior`][role-behavior] [classes][role-behavior-implementations]. ## Defining a role @@ -41,7 +41,7 @@ A role is defined by a `<role>` tag in `roles.xml`. The following attributes are available for role: - `name`: The unique name to identify the role, e.g. `android.app.role.SMS`. -- `behavior`: Optional name of a [`RoleBehavior`](model/RoleBehavior.java) class to control certain +- `behavior`: Optional name of a [`RoleBehavior`][role-behavior] class to control certain role behavior in Java code, e.g. `SmsRoleBehavior`. This can be useful when the XML syntax cannot express certain behavior specific to the role. - `defaultHolders`: Optional name of a system config resource that designates the default holders of @@ -58,6 +58,17 @@ the default app detail page as a footer. This attribute is required if the role allowed to be its holder. - `fallBackToDefaultHolder`: Whether the role should fall back to the default holder. This attribute is optional and defaults to `false`. +- `featureFlag`: Optional feature flag for the role be available, as the fully qualified name of +the Java method on the `Flags` class which will be invoked via reflection. Note that any new +aconfig library dependency will need corresponding jarjar rules for PermissionController and the +system service JAR. +- `ignoreDisabledSystemPackageWhenGranting`: Whether the role should ignore the requested +permissions of the disabled system package (if any) when granting permissions. If `false`, the +permission will need to be requested by the disabled system package as well, if there is one. This +attribute is optional and defaults to the opposite of `systemOnly` on Android S+, or `true` below +Android S. **Note:** Extra care should be taken when adding a runtime permission to a role with +this attribute explicitly set to `true`, because that may allow apps to update and silently obtain +a new runtime permission. - `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`. @@ -190,3 +201,7 @@ cmd role set-bypassing-role-qualification true|false The command outputs nothing and exits with `0` on success. If there was an error, the error will be printed and the command will terminate with a non-zero exit code. + +[role-behavior]: ../../../../../role-controller/java/com/android/role/controller/model/RoleBehavior.java +[role-behavior-implementations]: ../../../../../role-controller/java/com/android/role/controller/behavior/ +[roles-xml]: ../../../../../res/xml/roles.xml diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java index 145031b63..2f515f02c 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java @@ -17,6 +17,7 @@ package com.android.permissioncontroller.role.ui; import android.app.Activity; +import android.app.role.RoleManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -30,16 +31,19 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; +import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.role.utils.PackageUtils; import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; +import com.android.permissioncontroller.role.utils.SettingsCompat; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; @@ -63,6 +67,8 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat + ".preference.NONE"; private static final String PREFERENCE_KEY_DESCRIPTION = DefaultAppChildFragment.class.getName() + ".preference.DESCRIPTION"; + private static final String PREFERENCE_KEY_OTHER_NFC_SERVICES = + DefaultAppChildFragment.class.getName() + ".preference.OTHER_NFC_SERVICES"; @NonNull private String mRoleName; @@ -112,8 +118,9 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat mRole = Roles.get(activity).get(mRoleName); preferenceFragment.setTitle(getString(mRole.getLabelResource())); - mViewModel = ViewModelProviders.of(this, new DefaultAppViewModel.Factory(mRole, mUser, - activity.getApplication())).get(DefaultAppViewModel.class); + ViewModelProvider.Factory viewModelFactory = new DefaultAppViewModel.Factory(mRole, mUser, + activity.getApplication()); + mViewModel = new ViewModelProvider(this, viewModelFactory).get(DefaultAppViewModel.class); mViewModel.getRoleLiveData().observe(this, this::onRoleChanged); mViewModel.getManageRoleHolderStateLiveData().observe(this, this::onManageRoleHolderStateChanged); @@ -132,11 +139,6 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat preferenceScreen = preferenceManager.createPreferenceScreen(context); preferenceFragment.setPreferenceScreen(preferenceScreen); } else { - oldDescriptionPreference = preferenceScreen.findPreference(PREFERENCE_KEY_DESCRIPTION); - if (oldDescriptionPreference != null) { - preferenceScreen.removePreference(oldDescriptionPreference); - oldDescriptionPreference.setOrder(Preference.DEFAULT_ORDER); - } for (int i = preferenceScreen.getPreferenceCount() - 1; i >= 0; --i) { Preference preference = preferenceScreen.getPreference(i); @@ -167,13 +169,8 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat oldPreferences, preferenceScreen, context); } - Preference descriptionPreference = oldDescriptionPreference; - if (descriptionPreference == null) { - descriptionPreference = preferenceFragment.createFooterPreference(); - descriptionPreference.setKey(PREFERENCE_KEY_DESCRIPTION); - descriptionPreference.setSummary(mRole.getDescriptionResource()); - } - preferenceScreen.addPreference(descriptionPreference); + addNonPaymentNfcServicesPreference(preferenceScreen, oldPreferences, context); + addDescriptionPreference(preferenceScreen, oldPreferences); preferenceFragment.onPreferenceScreenChanged(); } @@ -264,6 +261,46 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat mViewModel.setDefaultApp(packageName); } + private void addNonPaymentNfcServicesPreference(@NonNull PreferenceScreen preferenceScreen, + @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) { + if (!(SdkLevel.isAtLeastV() && Objects.equals(mRoleName, RoleManager.ROLE_WALLET))) { + return; + } + + Intent intent = new Intent(SettingsCompat.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS); + if (!PackageUtils.isIntentResolvedToSettings(intent, context)) { + return; + } + + Preference preference = oldPreferences.get(PREFERENCE_KEY_OTHER_NFC_SERVICES); + if (preference == null) { + preference = new Preference(context); + preference.setKey(PREFERENCE_KEY_OTHER_NFC_SERVICES); + preference.setIcon(AppCompatResources.getDrawable(context, R.drawable.ic_nfc)); + preference.setTitle(context.getString( + R.string.default_payment_app_other_nfc_services)); + preference.setPersistent(false); + preference.setOnPreferenceClickListener(preference2 -> { + context.startActivity(intent); + return true; + }); + } + + preferenceScreen.addPreference(preference); + } + + private void addDescriptionPreference(@NonNull PreferenceScreen preferenceScreen, + @NonNull ArrayMap<String, Preference> oldPreferences) { + Preference preference = oldPreferences.get(PREFERENCE_KEY_DESCRIPTION); + if (preference == null) { + preference = requirePreferenceFragment().createFooterPreference(); + preference.setKey(PREFERENCE_KEY_DESCRIPTION); + preference.setSummary(mRole.getDescriptionResource()); + } + + preferenceScreen.addPreference(preference); + } + @NonNull private PF requirePreferenceFragment() { //noinspection unchecked diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java index 4fbe0ef33..48472bc5e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java @@ -17,20 +17,19 @@ package com.android.permissioncontroller.role.ui; import android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; +import android.permission.flags.Flags; import android.provider.Settings; import android.util.ArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceFragmentCompat; @@ -38,14 +37,15 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; +import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; +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.List; -import java.util.Objects; /** * Child fragment for the list of default apps. @@ -84,8 +84,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - - mViewModel = ViewModelProviders.of(this).get(DefaultAppListViewModel.class); + mViewModel = new ViewModelProvider(this).get(DefaultAppListViewModel.class); mViewModel.getLiveData().observe(this, roleItems -> onRoleListChanged()); if (mViewModel.hasWorkProfile()) { mViewModel.getWorkLiveData().observe(this, roleItems -> onRoleListChanged()); @@ -148,15 +147,25 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat addMoreDefaultAppsPreference(preferenceScreen, oldPreferences, context); addManageDomainUrlsPreference(preferenceScreen, oldPreferences, context); if (hasWorkProfile && !workRoleItems.isEmpty()) { + String defaultWorkTitle; + if (SdkLevel.isAtLeastV() && Flags.useProfileLabelsForDefaultAppSectionTitles()) { + defaultWorkTitle = Utils.getProfileLabel(mViewModel.getWorkProfile(), context); + } else { + defaultWorkTitle = context.getString(R.string.default_apps_for_work); + } String workTitle = Utils.getEnterpriseString(context, - DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE, - R.string.default_apps_for_work); + DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE, defaultWorkTitle); addPreferenceCategory(oldWorkPreferenceCategory, PREFERENCE_KEY_WORK_CATEGORY, workTitle, preferenceScreen, workRoleItems, oldWorkPreferences, this, mViewModel.getWorkProfile(), context); } if (hasPrivateProfile && !privateRoleItems.isEmpty()) { - String privateTitle = context.getString(R.string.default_apps_for_private_profile); + String privateTitle; + if (SdkLevel.isAtLeastV() && Flags.useProfileLabelsForDefaultAppSectionTitles()) { + privateTitle = Utils.getProfileLabel(mViewModel.getPrivateProfile(), context); + } else { + privateTitle = context.getString(R.string.default_apps_for_private_profile); + } addPreferenceCategory(oldPrivatePreferenceCategory, PREFERENCE_KEY_PRIVATE_CATEGORY, privateTitle, preferenceScreen, privateRoleItems, oldPrivatePreferences, this, mViewModel.getPrivateProfile(), context); @@ -263,7 +272,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat private static void addMoreDefaultAppsPreference(@NonNull PreferenceGroup preferenceGroup, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) { Intent intent = new Intent(Settings.ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS); - if (!isIntentResolvedToSettings(intent, context)) { + if (!PackageUtils.isIntentResolvedToSettings(intent, context)) { return; } @@ -286,7 +295,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat private static void addManageDomainUrlsPreference(@NonNull PreferenceGroup preferenceGroup, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) { Intent intent = new Intent(Settings.ACTION_MANAGE_DOMAIN_URLS); - if (!isIntentResolvedToSettings(intent, context)) { + if (!PackageUtils.isIntentResolvedToSettings(intent, context)) { return; } @@ -306,19 +315,6 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat preferenceGroup.addPreference(preference); } - private static boolean isIntentResolvedToSettings(@NonNull Intent intent, - @NonNull Context context) { - PackageManager packageManager = context.getPackageManager(); - ComponentName componentName = intent.resolveActivity(packageManager); - if (componentName == null) { - return false; - } - Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); - String settingsPackageName = settingsIntent.resolveActivity(packageManager) - .getPackageName(); - return Objects.equals(componentName.getPackageName(), settingsPackageName); - } - @NonNull private PF requirePreferenceFragment() { //noinspection unchecked diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java index fb3a46d6a..97ed74c01 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java @@ -45,7 +45,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.fragment.app.DialogFragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.permissioncontroller.R; @@ -230,8 +230,9 @@ public class RequestRoleFragment extends DialogFragment { mPackageRemovalMonitor.register(); // Postponed to onStart() so that the list view in dialog is created. - mViewModel = ViewModelProviders.of(this, new RequestRoleViewModel.Factory(mRole, - requireActivity().getApplication())).get(RequestRoleViewModel.class); + ViewModelProvider.Factory viewModelFactory = new RequestRoleViewModel.Factory(mRole, + requireActivity().getApplication()); + mViewModel = new ViewModelProvider(this, viewModelFactory).get(RequestRoleViewModel.class); mViewModel.getRoleLiveData().observe(this, this::onRoleDataChanged); mViewModel.getManageRoleHolderStateLiveData().observe(this, this::onManageRoleHolderStateChanged); diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java index aa6194997..f1754dde9 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java @@ -16,6 +16,7 @@ package com.android.permissioncontroller.role.ui.behavior.v35; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -28,6 +29,7 @@ import android.nfc.cardemulation.HostApduService; import android.nfc.cardemulation.OffHostApduService; import android.os.Build; import android.os.UserHandle; +import android.permission.flags.Flags; import android.text.TextUtils; import android.util.Log; @@ -82,8 +84,18 @@ public class WalletRoleUiBehavior implements RoleUiBehavior { List<ApduServiceInfo> serviceInfos = getNfcServicesForPackage( applicationInfo.packageName, user, context); - Pair<Drawable, CharSequence> bannerAndLabel = - getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context); + Pair<Drawable, CharSequence> bannerAndLabel = null; + // If the flag is enabled , attempt to fetch it from property + if (Flags.walletRoleIconPropertyEnabled()) { + bannerAndLabel = + getBannerAndLabelFromPackageProperty(context, user, + applicationInfo.packageName); + } + // If it's null, indicating that the property is not set, perform a legacy icon lookup. + if (bannerAndLabel == null) { + bannerAndLabel = + getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context); + } if (bannerAndLabel != null) { preference.setIcon(bannerAndLabel.first); if (setTitle) { @@ -95,6 +107,30 @@ public class WalletRoleUiBehavior implements RoleUiBehavior { } } + + @Nullable + private Pair<Drawable, CharSequence> getBannerAndLabelFromPackageProperty( + @NonNull Context context, @NonNull UserHandle user, @NonNull String packageName) { + List<ApduServiceInfo> apduServiceInfos = getNfcServicesForPackage(packageName, + user, context); + PackageManager packageManager = context.getPackageManager(); + int apduServiceInfoSize = apduServiceInfos.size(); + for (int i = 0; i < apduServiceInfoSize; i++) { + ApduServiceInfo serviceInfo = apduServiceInfos.get(i); + ComponentName componentName = serviceInfo.getComponent(); + try { + PackageManager.Property iconProperty = packageManager.getProperty( + ApduServiceInfo.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL, componentName); + if (iconProperty.isBoolean() && iconProperty.getBoolean()) { + return loadBannerAndLabel(serviceInfo, packageManager); + } + } catch (PackageManager.NameNotFoundException e) { + continue; + } + } + return null; + } + @NonNull private static List<ApduServiceInfo> getNfcServicesForPackage(@NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { 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 4b397343c..0963635e7 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java @@ -28,7 +28,7 @@ import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; @@ -103,8 +103,10 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat mRole = Roles.get(activity).get(mRoleName); preferenceFragment.setTitle(getString(mRole.getLabelResource())); - mViewModel = ViewModelProviders.of(this, new SpecialAppAccessViewModel.Factory(mRole, - activity.getApplication())).get(SpecialAppAccessViewModel.class); + ViewModelProvider.Factory viewModelFactory = new SpecialAppAccessViewModel.Factory(mRole, + activity.getApplication()); + mViewModel = new ViewModelProvider(this, viewModelFactory) + .get(SpecialAppAccessViewModel.class); mViewModel.getRoleLiveData().observe(this, this::onRoleChanged); mViewModel.observeManageRoleHolderState(this, this::onManageRoleHolderStateChanged); } 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 cacb4377f..22169abf2 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java @@ -26,7 +26,7 @@ import android.util.ArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; @@ -69,7 +69,7 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mViewModel = ViewModelProviders.of(this).get(SpecialAppAccessListViewModel.class); + mViewModel = new ViewModelProvider(this).get(SpecialAppAccessListViewModel.class); mViewModel.getLiveData().observe(this, roleItems -> onRoleListChanged()); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt index 06b4e61be..a47719cf7 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt @@ -44,7 +44,7 @@ class WearDefaultAppHelper( if (role.shouldShowNone()) { WearRoleApplicationPreference( context = context, - label = context.getString(R.string.default_app_none), + defaultLabel = context.getString(R.string.default_app_none), checked = !hasHolderApplication(qualifyingApplications), onDefaultCheckChanged = { _ -> viewModel.setNoneDefaultApp() } ) @@ -62,7 +62,7 @@ class WearDefaultAppHelper( val selected = pair.second WearRoleApplicationPreference( context = context, - label = Utils.getFullAppLabel(appInfo, context), + defaultLabel = Utils.getFullAppLabel(appInfo, context), checked = selected, onDefaultCheckChanged = { _ -> run { @@ -115,6 +115,7 @@ class WearDefaultAppHelper( confirmDialogViewModel.confirmDialogArgs = null confirmDialogViewModel.showConfirmDialogLiveData.value = false } + private fun setDefaultApp(packageName: String) { viewModel.setDefaultApp(packageName) } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt index a133aa2c3..5d4233c6e 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt @@ -63,7 +63,7 @@ private fun WearDefaultAppContent( helper.getNonePreference(qualifyingApplications)?.let { item { ToggleChip( - label = it.label, + label = it.title.toString(), icon = it.icon, checked = it.checked, onCheckedChanged = it.onDefaultCheckChanged, @@ -75,10 +75,10 @@ private fun WearDefaultAppContent( for (pref in helper.getPreferences(qualifyingApplications)) { item { ToggleChip( - label = pref.label, + label = pref.title.toString(), icon = pref.icon, colors = - if (pref.isEnabled()) { + if (pref.isEnabled) { ToggleChipDefaults.toggleChipColors() } else { toggleChipDisabledColors() diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt index 13a9cb6d6..aa9b31e0d 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt @@ -30,22 +30,26 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.wear.compose.material.ChipDefaults -import androidx.wear.compose.material.MaterialTheme import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.wear.elements.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl -import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionListFooter +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlStyle +import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3 import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData @Composable fun WearRequestRoleScreen( helper: WearRequestRoleHelper, onSetAsDefault: (Boolean, String?) -> Unit, - onCanceled: () -> Unit + onCanceled: () -> Unit, ) { val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList()) val manageRoleHolderState = @@ -74,8 +78,14 @@ fun WearRequestRoleScreen( helper.initializeSelectedPackageName() } } - + val materialUIVersion = + if (ResourceHelper.material3Enabled) { + MATERIAL3 + } else { + MATERIAL2_5 + } WearRequestRoleContent( + materialUIVersion, isLoading, helper, roleLiveData.value, @@ -85,7 +95,7 @@ fun WearRequestRoleScreen( onCheckedChanged, onDontAskAgainCheckedChanged, onSetAsDefault, - onCanceled + onCanceled, ) if (isLoading && roleLiveData.value.isNotEmpty()) { @@ -95,6 +105,7 @@ fun WearRequestRoleScreen( @Composable internal fun WearRequestRoleContent( + materialUIVersion: WearPermissionMaterialUIVersion, isLoading: Boolean, helper: WearRequestRoleHelper, qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>, @@ -104,56 +115,74 @@ internal fun WearRequestRoleContent( onCheckedChanged: (Boolean, String?, Boolean) -> Unit, onDontAskAgainCheckedChanged: (Boolean) -> Unit, onSetAsDefault: (Boolean, String?) -> Unit, - onCanceled: () -> Unit + onCanceled: () -> Unit, ) { ScrollableScreen( + materialUIVersion = materialUIVersion, image = helper.getIcon(), title = helper.getTitle(), showTimeText = false, - isLoading = isLoading + isLoading = isLoading, ) { - helper.getNonePreference(qualifyingApplications, selectedPackageName)?.let { + helper.getNonePreference(qualifyingApplications, selectedPackageName)?.let { pref -> item { - ToggleChip( - label = it.label, - icon = it.icon, - enabled = enabled && it.enabled, - checked = it.checked, + WearPermissionToggleControl( + materialUIVersion = materialUIVersion, + label = pref.label, + iconBuilder = pref.icon?.let { WearPermissionIconBuilder.builder(it) }, + enabled = enabled && pref.enabled, + checked = pref.checked, onCheckedChanged = { checked -> - run { onCheckedChanged(checked, it.packageName, it.isHolder) } + onCheckedChanged(checked, pref.packageName, pref.isHolder) }, toggleControl = ToggleChipToggleControl.Radio, - labelMaxLine = Integer.MAX_VALUE + labelMaxLines = Integer.MAX_VALUE, ) } - it.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } } + pref.subTitle?.let { subTitle -> + item { + WearPermissionListFooter( + materialUIVersion = materialUIVersion, + label = subTitle, + ) + } + } } for (pref in helper.getPreferences(qualifyingApplications, selectedPackageName)) { item { - ToggleChip( + WearPermissionToggleControl( + materialUIVersion = materialUIVersion, label = pref.label, - icon = pref.icon, + iconBuilder = pref.icon?.let { WearPermissionIconBuilder.builder(it) }, enabled = enabled && pref.enabled, checked = pref.checked, onCheckedChanged = { checked -> - run { onCheckedChanged(checked, pref.packageName, pref.isHolder) } + onCheckedChanged(checked, pref.packageName, pref.isHolder) }, toggleControl = ToggleChipToggleControl.Radio, ) } - pref.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } } + pref.subTitle?.let { subTitle -> + item { + WearPermissionListFooter( + materialUIVersion = materialUIVersion, + label = subTitle, + ) + } + } } if (helper.showDontAskButton()) { item { - ToggleChip( + WearPermissionToggleControl( + materialUIVersion = materialUIVersion, checked = dontAskAgain, enabled = enabled, onCheckedChanged = { checked -> run { onDontAskAgainCheckedChanged(checked) } }, label = stringResource(R.string.request_role_dont_ask_again), toggleControl = ToggleChipToggleControl.Checkbox, - colors = toggleChipBackgroundColors(), + style = WearPermissionToggleControlStyle.Transparent, modifier = Modifier.testTag("com.android.permissioncontroller:id/dont_ask_again"), ) @@ -163,17 +192,18 @@ internal fun WearRequestRoleContent( item { Spacer(modifier = Modifier.height(14.dp)) } item { - Chip( + WearPermissionButton( + materialUIVersion = materialUIVersion, label = stringResource(R.string.request_role_set_as_default), - textColor = MaterialTheme.colors.background, - colors = ChipDefaults.primaryChipColors(), + style = WearPermissionButtonStyle.Primary, enabled = helper.shouldSetAsDefaultEnabled(enabled), onClick = { onSetAsDefault(dontAskAgain, selectedPackageName) }, modifier = Modifier.testTag("android:id/button1"), ) } item { - Chip( + WearPermissionButton( + materialUIVersion = materialUIVersion, label = stringResource(R.string.cancel), enabled = enabled, onClick = { onCanceled() }, diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt index abaa33a56..6cd52f576 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt @@ -27,17 +27,21 @@ import com.android.permissioncontroller.role.ui.RoleApplicationPreference */ class WearRoleApplicationPreference( context: Context, - val label: String, + defaultLabel: String, val checked: Boolean, val onDefaultCheckChanged: (Boolean) -> Unit = {}, private var restrictionIntent: Intent? = null ) : TwoStatePreference(context), RoleApplicationPreference { + init { + title = defaultLabel + } + fun getOnCheckChanged(): (Boolean) -> Unit = restrictionIntent?.let { { _ -> context.startActivity(it) } } ?: onDefaultCheckChanged override fun setRestrictionIntent(restrictionIntent: Intent?) { this.restrictionIntent = restrictionIntent - setEnabled(restrictionIntent == null) + isEnabled = restrictionIntent == null } override fun asTwoStatePreference(): TwoStatePreference = this diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/PackageUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/PackageUtils.java index 9aaa07ee0..1bfde0b8b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/utils/PackageUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/role/utils/PackageUtils.java @@ -16,14 +16,19 @@ package com.android.permissioncontroller.role.utils; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.UserHandle; +import android.provider.Settings; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.Objects; + /** * Utility methods about application packages. */ @@ -66,4 +71,25 @@ public final class PackageUtils { @NonNull UserHandle user, @NonNull Context context) { return getApplicationInfo(packageName, UserUtils.getUserContext(context, user)); } + + /** + * Check whether an intent is resolved to an activity inside Settings. + * + * @param intent the intent to check + * @param context the {@code Context} to retrieve system services + * + * @return whether the intent is resolved to an activity inside Settings, + */ + public static boolean isIntentResolvedToSettings(@NonNull Intent intent, + @NonNull Context context) { + PackageManager packageManager = context.getPackageManager(); + ComponentName componentName = intent.resolveActivity(packageManager); + if (componentName == null) { + return false; + } + Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); + String settingsPackageName = settingsIntent.resolveActivity(packageManager) + .getPackageName(); + return Objects.equals(componentName.getPackageName(), settingsPackageName); + } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/SettingsCompat.java b/PermissionController/src/com/android/permissioncontroller/role/utils/SettingsCompat.java new file mode 100644 index 000000000..b2c7e7a81 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/utils/SettingsCompat.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.role.utils; + +import android.annotation.SuppressLint; +import android.os.Build; +import android.provider.Settings; + +import androidx.annotation.RequiresApi; + +/** + * Helper for accessing features in {@link Settings}. + */ +public class SettingsCompat { + + private SettingsCompat() {} + + /** + * @see Settings#ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS + */ + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + // The constant is inlined so it's fine to suppress the warning. + @SuppressLint("NewApi") + public static final String ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS = + Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS; +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java index 3ab4faa66..54cb86ff3 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java @@ -26,7 +26,7 @@ import android.view.ViewGroup; import androidx.annotation.RequiresApi; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceViewHolder; -import androidx.preference.SwitchPreference; +import androidx.preference.SwitchPreferenceCompat; import com.android.permissioncontroller.R; import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel; @@ -38,7 +38,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsVie * method will not register any changes while it appears disabled. */ @RequiresApi(TIRAMISU) -public class ClickableDisabledSwitchPreference extends SwitchPreference { +public class ClickableDisabledSwitchPreference extends SwitchPreferenceCompat { private boolean mAppearDisabled; diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java index 7622270b9..88759797e 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java @@ -52,6 +52,7 @@ 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.settingslib.widget.GroupSectionDividerMixin; import com.google.android.material.button.MaterialButton; import com.google.android.material.shape.AbsoluteCornerSize; @@ -62,7 +63,8 @@ import java.util.Objects; /** A preference that displays a card representing a {@link SafetyCenterIssue}. */ @RequiresApi(TIRAMISU) -public class IssueCardPreference extends Preference implements ComparablePreference { +public class IssueCardPreference extends Preference + implements ComparablePreference, GroupSectionDividerMixin { public static final String TAG = IssueCardPreference.class.getSimpleName(); @@ -101,12 +103,13 @@ public class IssueCardPreference extends Preference implements ComparablePrefere public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - holder.itemView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId()); + View issueCardView = holder.itemView.requireViewById(R.id.issue_card); + issueCardView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId()); int topMargin = getTopMargin(mPositionInCardList, getContext()); - MarginLayoutParams layoutParams = (MarginLayoutParams) holder.itemView.getLayoutParams(); + MarginLayoutParams layoutParams = (MarginLayoutParams) issueCardView.getLayoutParams(); if (layoutParams.topMargin != topMargin) { layoutParams.topMargin = topMargin; - holder.itemView.setLayoutParams(layoutParams); + issueCardView.setLayoutParams(layoutParams); } // Set default group visibility in case view is being reused @@ -202,19 +205,20 @@ public class IssueCardPreference extends Preference implements ComparablePrefere private void configureSafetyProtectionView(PreferenceViewHolder holder) { View safetyProtectionSectionView = holder.findViewById(R.id.issue_card_protected_by_android); + View issueCard = holder.findViewById(R.id.issue_card); if (safetyProtectionSectionView.getVisibility() == View.GONE) { - holder.itemView.setPaddingRelative( - holder.itemView.getPaddingStart(), - holder.itemView.getPaddingTop(), - holder.itemView.getPaddingEnd(), + issueCard.setPaddingRelative( + issueCard.getPaddingStart(), + issueCard.getPaddingTop(), + issueCard.getPaddingEnd(), /* bottom= */ getContext() .getResources() .getDimensionPixelSize(R.dimen.sc_card_margin_bottom)); } else { - holder.itemView.setPaddingRelative( - holder.itemView.getPaddingStart(), - holder.itemView.getPaddingTop(), - holder.itemView.getPaddingEnd(), + issueCard.setPaddingRelative( + issueCard.getPaddingStart(), + issueCard.getPaddingTop(), + issueCard.getPaddingEnd(), /* bottom= */ 0); } } @@ -427,9 +431,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere TypedValue buttonThemeValue = new TypedValue(); mContext.getTheme() .resolveAttribute( - R.attr.scActionButtonTheme, - buttonThemeValue, - /* resolveRefs= */ false); + R.attr.scActionButtonTheme, buttonThemeValue, /* resolveRefs= */ false); mContextThemeWrapper = new ContextThemeWrapper(context, buttonThemeValue.data); } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt index a63e19984..5c86b4515 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt @@ -24,6 +24,7 @@ import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.R import com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView +import com.android.settingslib.widget.GroupSectionDividerMixin /** A preference that displays a card linking to a list of more {@link SafetyCenterIssue}. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @@ -34,8 +35,8 @@ internal class MoreIssuesCardPreference( private var newMoreIssuesCardData: MoreIssuesCardData, private val dismissedOnly: Boolean, val isStaticHeader: Boolean, - private val onClickListener: () -> Unit -) : Preference(context), ComparablePreference { + private val onClickListener: () -> Unit, +) : Preference(context), ComparablePreference, GroupSectionDividerMixin { init { layoutResource = R.layout.preference_more_issues_card @@ -44,11 +45,12 @@ internal class MoreIssuesCardPreference( override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) - val issueHeaderView = holder.itemView as MoreIssuesHeaderView + val issueHeaderView = + holder.itemView.requireViewById<MoreIssuesHeaderView>(R.id.more_issues_card) if (isStaticHeader) { issueHeaderView.showStaticHeader( context.getString(R.string.safety_center_dismissed_issues_card_title), - newMoreIssuesCardData.severityLevel + newMoreIssuesCardData.severityLevel, ) } else { issueHeaderView.showExpandableHeader( @@ -62,7 +64,7 @@ internal class MoreIssuesCardPreference( } ), overrideChevronIconResId, - onClickListener + onClickListener, ) } } @@ -94,5 +96,5 @@ internal class MoreIssuesCardPreference( internal data class MoreIssuesCardData( val severityLevel: Int, val hiddenIssueCount: Int, - val isExpanded: Boolean + val isExpanded: Boolean, ) diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt index 57e4175ca..c5287af53 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt @@ -28,11 +28,12 @@ import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID import com.android.permissioncontroller.R import com.android.permissioncontroller.safetycenter.SafetyCenterConstants +import com.android.settingslib.widget.GroupSectionDividerMixin /** A preference that displays the Security and Privacy brand name on a Safety Center subpage. */ @RequiresApi(UPSIDE_DOWN_CAKE) internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet) : - Preference(context, attrs) { + Preference(context, attrs), GroupSectionDividerMixin { init { setLayoutResource(R.layout.preference_brand_chip) @@ -67,7 +68,7 @@ internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet) fun closeSubpage( fragmentActivity: FragmentActivity, fragmentContext: Context, - sessionId: Long + sessionId: Long, ) { val openedFromHomepage = fragmentActivity diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java index b873bc114..04206479f 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java @@ -29,6 +29,8 @@ import static com.android.permissioncontroller.safetycenter.SafetyCenterConstant import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX; import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX; +import static java.util.Objects.requireNonNull; + import android.app.ActionBar; import android.content.Intent; import android.content.res.Configuration; @@ -40,10 +42,18 @@ import android.safetycenter.config.SafetySource; import android.safetycenter.config.SafetySourcesGroup; import android.text.TextUtils; import android.util.Log; +import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceFragmentCompat; import com.android.permissioncontroller.Constants; import com.android.permissioncontroller.PermissionControllerStatsLog; @@ -52,6 +62,7 @@ import com.android.permissioncontroller.permission.utils.Utils; import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.Pref; import com.android.settingslib.activityembedding.ActivityEmbeddingUtils; import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; +import com.android.settingslib.widget.SettingsThemeHelper; import java.util.List; import java.util.Objects; @@ -70,10 +81,10 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { private static final String EXTRA_PREVENT_TRAMPOLINE_TO_SETTINGS = "com.android.permissioncontroller.safetycenter.extra.PREVENT_TRAMPOLINE_TO_SETTINGS"; - private SafetyCenterManager mSafetyCenterManager; + @Nullable private SafetyCenterManager mSafetyCenterManager; @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSafetyCenterManager = getSystemService(SafetyCenterManager.class); @@ -85,18 +96,25 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { return; } + if (SettingsThemeHelper.isExpressiveTheme(this)) { + // Setting a theme programmatically causes standard preferences to display weirdly. + // See b/377519324. + setTheme(R.style.Theme_SafetyCenterExpressive); + } + Fragment frag; + Intent intent = getIntent(); final boolean maybeOpenSubpage = SafetyCenterUiFlags.getShowSubpages() - && getIntent().getAction().equals(ACTION_SAFETY_CENTER); - if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) { - String groupId = getIntent().getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID); + && Objects.equals(intent.getAction(), ACTION_SAFETY_CENTER); + if (maybeOpenSubpage && intent.hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) { + String groupId = intent.getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID); frag = openRelevantSubpage(groupId); - } else if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) { - String preferenceKey = getIntent().getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY); + } else if (maybeOpenSubpage && intent.hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) { + String preferenceKey = intent.getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY); String groupId = getParentGroupId(preferenceKey); frag = openRelevantSubpage(groupId); - } else if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) { + } else if (Objects.equals(intent.getAction(), PRIVACY_CONTROLS_ACTION)) { setTitle(R.string.privacy_controls_title); frag = PrivacyControlsFragment.newInstance(); } else { @@ -111,6 +129,36 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { } configureHomeButton(); + + frag.getLifecycle() + .addObserver( + new LifecycleEventObserver() { + @Override + public void onStateChanged( + LifecycleOwner unused, Lifecycle.Event event) { + if (event != Lifecycle.Event.ON_START) { + return; + } + View listView = getListView(frag); + if (listView == null) { + return; + } + int paddingBottom = listView.getPaddingBottom(); + ViewCompat.setOnApplyWindowInsetsListener( + listView, + (v, windowInsets) -> { + Insets insets = + windowInsets.getInsets( + WindowInsetsCompat.Type.systemBars()); + v.setPadding( + v.getPaddingLeft(), + v.getPaddingTop(), + v.getPaddingRight(), + paddingBottom + insets.bottom); + return WindowInsetsCompat.CONSUMED; + }); + } + }); } @Override @@ -129,8 +177,10 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { /** Decide whether a home/back button should be shown or not. */ private void configureHomeButton() { ActionBar actionBar = getActionBar(); - Fragment frag = getSupportFragmentManager().findFragmentById( - com.android.settingslib.collapsingtoolbar.R.id.content_frame); + Fragment frag = + getSupportFragmentManager() + .findFragmentById( + com.android.settingslib.collapsingtoolbar.R.id.content_frame); if (actionBar == null || frag == null) { return; } @@ -266,7 +316,8 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { return PRIVACY_SOURCES_GROUP_ID; } - SafetyCenterConfig safetyCenterConfig = mSafetyCenterManager.getSafetyCenterConfig(); + SafetyCenterConfig safetyCenterConfig = + requireNonNull(mSafetyCenterManager).getSafetyCenterConfig(); String[] splitKey; if (preferenceKey.endsWith(PERSONAL_PROFILE_SUFFIX)) { splitKey = preferenceKey.split("_" + PERSONAL_PROFILE_SUFFIX); @@ -296,4 +347,19 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { } return ""; } + + @Nullable + private View getListView(Fragment fragment) { + if (fragment instanceof PreferenceFragmentCompat) { + return ((PreferenceFragmentCompat) fragment).getListView(); + } + if (fragment instanceof SafetyCenterScrollWrapperFragment) { + Fragment dashboardFragment = + fragment.getChildFragmentManager().findFragmentById(R.id.fragment_container); + if (dashboardFragment instanceof PreferenceFragmentCompat) { + return ((PreferenceFragmentCompat) dashboardFragment).getListView(); + } + } + return null; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java index efbd57080..ed6bc382c 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java @@ -56,6 +56,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; import com.android.safetycenter.internaldata.SafetyCenterBundles; import com.android.safetycenter.resources.SafetyCenterResourcesApk; +import com.android.settingslib.widget.SettingsThemeHelper; import kotlin.Unit; @@ -121,12 +122,16 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment { mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY); mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY); + Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY); + if (SettingsThemeHelper.isExpressiveTheme(requireContext())) { + getPreferenceScreen().removePreference(spacerPreference); + } + if (mIsQuickSettingsFragment) { getPreferenceScreen().removePreference(mEntriesGroup); mEntriesGroup = null; getPreferenceScreen().removePreference(mStaticEntriesGroup); mStaticEntriesGroup = null; - Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY); getPreferenceScreen().removePreference(spacerPreference); } getSafetyCenterViewModel().getStatusUiLiveData().observe(this, this::updateStatus); diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt index 9feecf5d4..04503de5e 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt @@ -22,7 +22,6 @@ import android.safetycenter.SafetyCenterErrorDetails import android.widget.Toast import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModelProvider -import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import androidx.recyclerview.widget.RecyclerView import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID @@ -33,10 +32,12 @@ import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterVi import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel import com.android.safetycenter.resources.SafetyCenterResourcesApk +import com.android.settingslib.widget.SettingsBasePreferenceFragment +import com.android.settingslib.widget.SettingsThemeHelper /** A base fragment that represents a page in Safety Center. */ @RequiresApi(TIRAMISU) -abstract class SafetyCenterFragment : PreferenceFragmentCompat() { +abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() { lateinit var safetyCenterViewModel: SafetyCenterViewModel lateinit var sameTaskSourceIds: List<String> @@ -51,12 +52,17 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { override fun onCreateAdapter( preferenceScreen: PreferenceScreen - ): RecyclerView.Adapter<RecyclerView.ViewHolder> { + ): RecyclerView.Adapter<out RecyclerView.ViewHolder> { /* The scroll-to-result functionality for settings search is currently implemented only for * subpages i.e. non expand-and-collapse type entries. Hence, we check that the flag is * enabled before using an adapter that does the highlighting and scrolling. */ - val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = - if (SafetyCenterUiFlags.getShowSubpages()) { + val adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder> = + if ( + SafetyCenterUiFlags.getShowSubpages() && + !SettingsThemeHelper.isExpressiveTheme(requireContext()) + ) { + // TODO: b/378433878 - Create highlight adapter for settings expressive theme, which + // has a different base class. highlightManager.createAdapter(preferenceScreen) } else { super.onCreateAdapter(preferenceScreen) @@ -80,7 +86,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { safetyCenterViewModel = ViewModelProvider( requireActivity(), - LiveSafetyCenterViewModelFactory(requireActivity().getApplication()) + LiveSafetyCenterViewModelFactory(requireActivity().getApplication()), ) .get(SafetyCenterViewModel::class.java) safetyCenterViewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? -> @@ -177,7 +183,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { safetyCenterViewModel.interactionLogger.recordForIssue( Action.SAFETY_CENTER_VIEWED, maybeIssue, - isDismissed = false + isDismissed = false, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java index 2ad282449..d9f45cc08 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java @@ -24,7 +24,9 @@ import android.permission.PermissionManager; import androidx.fragment.app.FragmentActivity; import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; +import com.android.settingslib.widget.SettingsThemeHelper; /** Activity for the Safety Center Quick Settings Activity */ public class SafetyCenterQsActivity extends FragmentActivity { @@ -39,6 +41,12 @@ public class SafetyCenterQsActivity extends FragmentActivity { return; } + if (SettingsThemeHelper.isExpressiveTheme(this)) { + // Safe to set expressive theme here since QS doesn't display vanilla preferences. + // See b/377519324. + setTheme(R.style.Theme_SafetyCenterQsExpressive); + } + configureFragment(); } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt index fdade2189..2e5ba49ae 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt @@ -28,6 +28,7 @@ import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreferenc import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData import com.android.safetycenter.resources.SafetyCenterResourcesApk import com.android.settingslib.widget.FooterPreference +import com.android.settingslib.widget.SettingsThemeHelper /** A fragment that represents a generic subpage in Safety Center. */ @RequiresApi(UPSIDE_DOWN_CAKE) @@ -45,15 +46,16 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { setPreferencesFromResource(R.xml.safety_center_subpage, rootKey) sourceGroupId = requireArguments().getString(SOURCE_GROUP_ID_KEY)!! - subpageBrandChip = getPreferenceScreen().findPreference(BRAND_CHIP_KEY)!! - subpageIllustration = getPreferenceScreen().findPreference(ILLUSTRATION_KEY)!! - subpageIssueGroup = getPreferenceScreen().findPreference(ISSUE_GROUP_KEY)!! - subpageEntryGroup = getPreferenceScreen().findPreference(ENTRY_GROUP_KEY)!! - subpageFooter = getPreferenceScreen().findPreference(FOOTER_KEY)!! + subpageBrandChip = preferenceScreen.findPreference(BRAND_CHIP_KEY)!! + subpageIllustration = preferenceScreen.findPreference(ILLUSTRATION_KEY)!! + subpageIssueGroup = preferenceScreen.findPreference(ISSUE_GROUP_KEY)!! + subpageEntryGroup = preferenceScreen.findPreference(ENTRY_GROUP_KEY)!! + subpageFooter = preferenceScreen.findPreference(FOOTER_KEY)!! subpageBrandChip.setupListener(requireActivity(), safetyCenterSessionId) setupIllustration() setupFooter() + maybeRemoveSpacer() prerenderCurrentSafetyCenterData() } @@ -80,7 +82,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { return } - requireActivity().setTitle(entryGroup.title) + requireActivity().title = entryGroup.title updateSafetyCenterIssues(uiData) updateSafetyCenterEntries(entryGroup) } @@ -91,7 +93,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { val drawable = SafetyCenterResourcesApk(context).getDrawableByName(resName, context.theme) if (drawable == null) { Log.w(TAG, "$sourceGroupId doesn't have any matching illustration") - subpageIllustration.setVisible(false) + subpageIllustration.isVisible = false } subpageIllustration.illustrationDrawable = drawable @@ -102,12 +104,19 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { val footerText = SafetyCenterResourcesApk(requireContext()).getStringByName(resName) if (footerText.isEmpty()) { Log.w(TAG, "$sourceGroupId doesn't have any matching footer") - subpageFooter.setVisible(false) + subpageFooter.isVisible = false } // footer is ordered last by default // in order to keep a spacer after the footer, footer needs to be the second from last - subpageFooter.setOrder(Int.MAX_VALUE - 2) - subpageFooter.setSummary(footerText) + subpageFooter.order = Int.MAX_VALUE - 2 + subpageFooter.summary = footerText + } + + private fun maybeRemoveSpacer() { + if (SettingsThemeHelper.isExpressiveTheme(requireContext())) { + val spacerPreference = preferenceScreen.findPreference<SpacerPreference>(SPACER_KEY)!! + preferenceScreen.removePreference(spacerPreference) + } } private fun updateSafetyCenterIssues(uiData: SafetyCenterUiData?) { @@ -131,7 +140,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { subpageIssues, subpageDismissedIssues, uiData.resolvedIssues, - requireActivity().getTaskId() + requireActivity().taskId, ) } @@ -145,23 +154,24 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { PendingIntentSender.getTaskIdForEntry( entry.id, sameTaskSourceIds, - requireActivity() + requireActivity(), ), entry, - safetyCenterViewModel + safetyCenterViewModel, ) ) } } companion object { - private val TAG: String = SafetyCenterSubpageFragment::class.java.simpleName - private const val BRAND_CHIP_KEY: String = "subpage_brand_chip" - private const val ILLUSTRATION_KEY: String = "subpage_illustration" - private const val ISSUE_GROUP_KEY: String = "subpage_issue_group" - private const val ENTRY_GROUP_KEY: String = "subpage_entry_group" - private const val FOOTER_KEY: String = "subpage_footer" - private const val SOURCE_GROUP_ID_KEY: String = "source_group_id" + private val TAG = SafetyCenterSubpageFragment::class.java.simpleName + private const val BRAND_CHIP_KEY = "subpage_brand_chip" + private const val ILLUSTRATION_KEY = "subpage_illustration" + private const val ISSUE_GROUP_KEY = "subpage_issue_group" + private const val ENTRY_GROUP_KEY = "subpage_entry_group" + private const val FOOTER_KEY = "subpage_footer" + private const val SPACER_KEY = "subpage_spacer" + private const val SOURCE_GROUP_ID_KEY = "source_group_id" /** Creates an instance of SafetyCenterSubpageFragment with the arguments set */ @JvmStatic diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt index 5acb27131..3976fa7e3 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt @@ -25,11 +25,12 @@ import androidx.annotation.RequiresApi import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.R +import com.android.settingslib.widget.GroupSectionDividerMixin /** A preference that displays the illustration on a Safety Center subpage. */ @RequiresApi(UPSIDE_DOWN_CAKE) internal class SafetyIllustrationPreference(context: Context, attrs: AttributeSet) : - Preference(context, attrs) { + Preference(context, attrs), GroupSectionDividerMixin { init { layoutResource = R.layout.preference_illustration diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java index 811841845..abf159955 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java @@ -40,6 +40,7 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; import com.android.permissioncontroller.safetycenter.ui.view.StatusCardView; +import com.android.settingslib.widget.GroupSectionDividerMixin; import kotlin.Pair; @@ -48,7 +49,8 @@ import java.util.Objects; /** Preference which displays a visual representation of {@link SafetyCenterStatus}. */ @RequiresApi(TIRAMISU) -public class SafetyStatusPreference extends Preference implements ComparablePreference { +public class SafetyStatusPreference extends Preference + implements ComparablePreference, GroupSectionDividerMixin { private static final String TAG = "SafetyStatusPreference"; @@ -82,7 +84,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } Context context = getContext(); - StatusCardView statusCardView = (StatusCardView) holder.itemView; + StatusCardView statusCardView = holder.itemView.requireViewById(R.id.status_card); + configureButtons(context, statusCardView); statusCardView .getTitleAndSummaryContainerView() diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt index 030b67be9..7f619d1ca 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt @@ -55,6 +55,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) : } private var maxKnownToolbarHeight = 0 + override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) val spacer = holder.itemView @@ -74,7 +75,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) : oldLeft: Int, oldTop: Int, oldRight: Int, - oldBottom: Int + oldBottom: Int, ) { adjustHeight(spacer) } diff --git a/PermissionController/tests/inprocess/Android.bp b/PermissionController/tests/inprocess/Android.bp index 7227e41ad..4cd9e0e6f 100644 --- a/PermissionController/tests/inprocess/Android.bp +++ b/PermissionController/tests/inprocess/Android.bp @@ -37,11 +37,12 @@ android_test { srcs: ["src/**/*.kt"], libs: [ - "android.test.base", - "android.test.runner", + "android.test.base.stubs.system", + "android.test.runner.stubs.system", ], static_libs: [ + "androidx.arch.core_core-testing", "androidx.test.rules", "androidx.test.ext.truth", "androidx.test.ext.junit", @@ -49,6 +50,9 @@ android_test { "compatibility-device-util-axt", "kotlin-test", "permission-test-util-lib", + // This may result in two flag libs being included. This should only be used for Flag + //string referencing for test annotations. + "com.android.permission.flags-aconfig-java-export", ], data: [ diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt new file mode 100644 index 000000000..eb5fdefdb --- /dev/null +++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/ui/model/PermissionUsageDetailsViewModelTest.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.model + +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.SavedStateHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelV2 +import com.google.common.truth.Truth +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.Assume +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** This is an integration tests for permission timeline page view model. */ +@RunWith(AndroidJUnit4::class) +class PermissionUsageDetailsViewModelTest { + @JvmField @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @JvmField @Rule val instantTaskExecutorRule = InstantTaskExecutorRule() + + @Test + @RequiresFlagsEnabled(Flags.FLAG_LIVEDATA_REFACTOR_PERMISSION_TIMELINE_ENABLED) + fun verifyUiStateIsGeneratedSuccessfully() { + Assume.assumeTrue(SdkLevel.isAtLeastS()) + lateinit var uiState: PermissionUsageDetailsUiState.Success + val viewModel = + PermissionUsageDetailsViewModelV2.create( + PermissionControllerApplication.get(), + SavedStateHandle(mapOf("show7Days" to true, "showSystem" to true)), + LOCATION_PERMISSION_GROUP + ) + val countDownLatch = CountDownLatch(1) + + viewModel.getPermissionUsagesDetailsInfoUiLiveData().observeForever { + if (it is PermissionUsageDetailsUiState.Success) { + uiState = it + countDownLatch.countDown() + } + } + countDownLatch.await(5L, TimeUnit.SECONDS) + Truth.assertThat(uiState).isNotNull() + } + + companion object { + private val LOCATION_PERMISSION_GROUP = android.Manifest.permission_group.LOCATION + } +} diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp index 3d425f5f8..37851b2bb 100644 --- a/PermissionController/tests/mocking/Android.bp +++ b/PermissionController/tests/mocking/Android.bp @@ -42,58 +42,14 @@ android_test { ], libs: [ - "android.test.base", - "android.test.runner", + "android.test.base.stubs.system", + "android.test.runner.stubs.system", "safety-center-annotations", ], static_libs: [ "PermissionController-lib", - "iconloader_sc_mainline_prod", - "com.google.android.material_material", - "androidx.transition_transition", - "androidx.compose.foundation_foundation", - "androidx.compose.runtime_runtime", - "androidx.compose.runtime_runtime-livedata", - "androidx.compose.ui_ui", - "androidx-constraintlayout_constraintlayout", - "androidx.core_core", - "androidx.media_media", - "androidx.legacy_legacy-support-core-utils", - "androidx.legacy_legacy-support-core-ui", - "androidx.fragment_fragment", - "androidx.appcompat_appcompat", - "androidx.preference_preference", - "androidx.recyclerview_recyclerview", - "androidx.legacy_legacy-preference-v14", - "androidx.leanback_leanback", - "androidx.leanback_leanback-preference", "androidx.lifecycle_lifecycle-extensions", - "androidx.lifecycle_lifecycle-common-java8", - "androidx.wear.compose_compose-material", - "kotlin-stdlib", - "kotlinx-coroutines-android", - "androidx.navigation_navigation-common-ktx", - "androidx.navigation_navigation-fragment-ktx", - "androidx.navigation_navigation-runtime-ktx", - "androidx.navigation_navigation-ui-ktx", - "SettingsLibHelpUtils", - "SettingsLibRestrictedLockUtils", - "SettingsLibAppPreference", - "SettingsLibSearchWidget", - "SettingsLibLayoutPreference", - "SettingsLibBarChartPreference", - "SettingsLibActionBarShadow", - "SettingsLibProgressBar", - "SettingsLibCollapsingToolbarBaseActivity", - "SettingsLibSettingsTheme", - "SettingsLibFooterPreference", - "SettingsLibSelectorWithWidgetPreference", - "SettingsLibTwoTargetPreference", - "SettingsLibActivityEmbedding", - "SettingsLibIllustrationPreference", - "androidx.annotation_annotation", - "permissioncontroller-statsd", // The PermissionController build file includes android.car-stubs in its libs dependency // and that works since the phone code does not initialize any classes with a android.car // dependency at runtime. If it did, the class loading would throw an exception. The "libs" @@ -107,16 +63,6 @@ android_test { // mocked, then the stubs dependency would be suitable. Note that on Auto device the boot // class path will always override the static dependency. "android.car", - "car-ui-lib", - "libprotobuf-java-lite", - "SettingsLibUtils", - "modules-utils-build_system", - "safety-center-internal-data", - "safety-center-pending-intents", - "safety-center-resources-lib", - "safety-label", - "role-controller", - "lottie", "android.permission.flags-aconfig-java-export", "com.android.permission.flags-aconfig-java-export", "platform-test-annotations", @@ -140,4 +86,7 @@ android_test { ], kotlincflags: ["-Xjvm-default=all"], + + // TODO(b/313706381): Remove jarjar once flagging lib is fixed + jarjar_rules: ":PermissionController-jarjar-rules", } diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt index 1077293d1..afb779c97 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt @@ -16,9 +16,21 @@ package com.android.permissioncontroller.tests.mocking.appops.data.repository +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf -class FakeAppOpRepository(override val packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>>) : - AppOpRepository +class FakeAppOpRepository( + override val packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>>, + private val discreteOps: Flow<List<DiscretePackageOpsModel>> = flowOf(), +) : AppOpRepository { + override fun getDiscreteOps( + opNames: List<String>, + coroutineScope: CoroutineScope + ): Flow<List<DiscretePackageOpsModel>> { + return discreteOps + } +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt index d85cb40b0..15a37532f 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt @@ -16,15 +16,27 @@ package com.android.permissioncontroller.tests.mocking.hibernation +import android.Manifest import android.app.job.JobScheduler +import android.content.ComponentName +import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.SharedPreferences +import android.content.pm.PackageManager +import android.content.pm.PackageManager.PERMISSION_GRANTED +import android.database.ContentObserver +import android.net.Uri +import android.os.Binder import android.os.Build import android.os.SystemClock +import android.os.UserHandle import android.os.UserManager import android.preference.PreferenceManager import android.provider.DeviceConfig +import android.provider.Settings +import android.telecom.PhoneAccountHandle +import android.telecom.TelecomManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress @@ -33,25 +45,35 @@ import com.android.permissioncontroller.Constants import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.hibernation.HibernationBroadcastReceiver import com.android.permissioncontroller.hibernation.ONE_DAY_MS -import com.android.permissioncontroller.hibernation.PREF_KEY_BOOT_TIME_SNAPSHOT import com.android.permissioncontroller.hibernation.PREF_KEY_ELAPSED_REALTIME_SNAPSHOT import com.android.permissioncontroller.hibernation.PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING +import com.android.permissioncontroller.hibernation.PREF_KEY_SYSTEM_TIME_SNAPSHOT import com.android.permissioncontroller.hibernation.SNAPSHOT_UNINITIALIZED import com.android.permissioncontroller.hibernation.getStartTimeOfUnusedAppTracking +import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBySystem +import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo +import com.android.permissioncontroller.permission.utils.ContextCompat import com.google.common.truth.Truth.assertThat -import java.io.File +import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString +import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.MockitoSession import org.mockito.quality.Strictness +import java.io.File /** Unit tests for [HibernationPolicy]. */ @RunWith(AndroidJUnit4::class) @@ -60,17 +82,24 @@ class HibernationPolicyTest { companion object { private val application = Mockito.mock(PermissionControllerApplication::class.java) + private const val USER_SETUP_INCOMPLETE = 0 + private const val USER_SETUP_COMPLETE = 1 + private const val TEST_PKG_NAME = "test.package" } @Mock lateinit var jobScheduler: JobScheduler @Mock lateinit var context: Context @Mock lateinit var userManager: UserManager + @Mock lateinit var packageManager: PackageManager + @Mock lateinit var telecomManager: TelecomManager + @Mock lateinit var contentResolver: ContentResolver private lateinit var realContext: Context private lateinit var receiver: HibernationBroadcastReceiver private lateinit var sharedPreferences: SharedPreferences private lateinit var mockitoSession: MockitoSession private lateinit var filesDir: File + private lateinit var userHandle: UserHandle @Before fun setup() { @@ -79,20 +108,29 @@ class HibernationPolicyTest { ExtendedMockito.mockitoSession() .mockStatic(PermissionControllerApplication::class.java) .mockStatic(DeviceConfig::class.java) + .mockStatic(Settings.Secure::class.java) .strictness(Strictness.LENIENT) .startMocking() `when`(PermissionControllerApplication.get()).thenReturn(application) + `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt())) + .thenReturn(USER_SETUP_COMPLETE) + `when`(Settings.Secure.getUriFor(any())).thenReturn(Mockito.mock(Uri::class.java)) realContext = ApplicationProvider.getApplicationContext() + userHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid()) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(realContext.applicationContext) `when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences) `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager) + `when`(application.getSystemService(TelecomManager::class.java)).thenReturn(telecomManager) + `when`(application.packageManager).thenReturn(packageManager) + `when`(application.applicationContext).thenReturn(context) filesDir = realContext.cacheDir `when`(application.filesDir).thenReturn(filesDir) `when`(context.getSystemService(JobScheduler::class.java)).thenReturn(jobScheduler) + `when`(context.contentResolver).thenReturn(contentResolver) `when`(jobScheduler.schedule(Mockito.any())).thenReturn(JobScheduler.RESULT_SUCCESS) receiver = HibernationBroadcastReceiver() @@ -101,11 +139,53 @@ class HibernationPolicyTest { @After fun cleanup() { mockitoSession.finishMocking() + sharedPreferences.edit().clear().commit() val logFile = File(filesDir, Constants.LOGS_TO_DUMP_FILE) logFile.delete() } @Test + fun onReceive_userSetupIncomplete_doesNotInitializeStartTime() { + `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt())) + .thenReturn(USER_SETUP_INCOMPLETE) + + receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED)) + + val startTimeOfUnusedAppTracking = + sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) + assertThat(startTimeOfUnusedAppTracking).isEqualTo(SNAPSHOT_UNINITIALIZED) + } + + @Test + fun onReceive_userSetupCompletes_initializesStartTime() { + `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt())) + .thenReturn(USER_SETUP_INCOMPLETE) + + receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED)) + + val contentObserverCaptor = ArgumentCaptor.forClass(ContentObserver::class.java) + val uri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE) + verify(contentResolver).registerContentObserver( + eq(uri), + anyBoolean(), + contentObserverCaptor.capture()) + val contentObserver = contentObserverCaptor.value + `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt())) + .thenReturn(USER_SETUP_COMPLETE) + contentObserver.onChange(/* selfChange= */ false, uri) + + val startTimeOfUnusedAppTracking = + sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) + assertThat(startTimeOfUnusedAppTracking).isNotEqualTo(SNAPSHOT_UNINITIALIZED) + } + + @Test fun onReceive_shouldInitializeAndAdjustStartTimeOfUnusedAppTracking() { receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED)) val startTimeOfUnusedAppTracking = @@ -114,7 +194,7 @@ class HibernationPolicyTest { SNAPSHOT_UNINITIALIZED ) val systemTimeSnapshot = - sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) + sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) val realtimeSnapshot = sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) val currentTimeMillis = System.currentTimeMillis() @@ -139,15 +219,39 @@ class HibernationPolicyTest { .isNotEqualTo(SNAPSHOT_UNINITIALIZED) receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED)) val systemTimeSnapshot = - sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) + sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) sharedPreferences .edit() - .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot - ONE_DAY_MS) + .putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTimeSnapshot - ONE_DAY_MS) .apply() assertThat(getStartTimeOfUnusedAppTracking(sharedPreferences)) .isNotEqualTo(systemTimeSnapshot) } + @Test + @Ignore("b/371061181") + // This method under test initializes several SmartAsyncMediatorLiveData classes which run code + // on GlobalScope which the unit test has no control over. This can lead to the code running + // during other tests which may not have the right static mocks. + // Until this is fixed, this test should be ignored to prevent flaky test faliures. + fun isPackageExemptBySystem_isCallingApp_returnsTrue() = runBlocking<Unit> { + val pkgInfo = makePackageInfo(TEST_PKG_NAME) + + `when`(context.checkPermission( + eq(Manifest.permission.MANAGE_OWN_CALLS), anyInt(), eq(pkgInfo.uid))) + .thenReturn(PERMISSION_GRANTED) + `when`(context.checkPermission( + eq(Manifest.permission.RECORD_AUDIO), anyInt(), eq(pkgInfo.uid))) + .thenReturn(PERMISSION_GRANTED) + `when`(context.checkPermission( + eq(Manifest.permission.WRITE_CALL_LOG), anyInt(), eq(pkgInfo.uid))) + .thenReturn(PERMISSION_GRANTED) + `when`(telecomManager.selfManagedPhoneAccounts).thenReturn( + listOf(PhoneAccountHandle(ComponentName(TEST_PKG_NAME, "Service"), "id"))) + + assertThat(isPackageHibernationExemptBySystem(pkgInfo, userHandle)).isTrue() + } + private fun assertAdjustedTime(systemTimeSnapshot: Long, realtimeSnapshot: Long) { val newStartTimeOfUnusedAppTracking = sharedPreferences.getLong( @@ -155,7 +259,7 @@ class HibernationPolicyTest { SNAPSHOT_UNINITIALIZED ) val newSystemTimeSnapshot = - sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) + sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) val newRealtimeSnapshot = sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) assertThat(newStartTimeOfUnusedAppTracking).isNotEqualTo(SNAPSHOT_UNINITIALIZED) @@ -164,4 +268,23 @@ class HibernationPolicyTest { assertThat(newRealtimeSnapshot).isNotEqualTo(SNAPSHOT_UNINITIALIZED) assertThat(newRealtimeSnapshot).isAtLeast(realtimeSnapshot) } + + private fun makePackageInfo(packageName: String): LightPackageInfo { + return LightPackageInfo( + packageName, + emptyList(), + emptyList(), + emptyList(), + 0 /* uid */, + Build.VERSION_CODES.CUR_DEVELOPMENT, + false /* isInstantApp */, + true /* enabled */, + 0 /* appFlags */, + 0 /* firstInstallTime */, + 0 /* lastUpdateTime */, + false /* areAttributionsUserVisible */, + emptyMap() /* attributionTagsToLabels */, + ContextCompat.DEVICE_ID_DEFAULT + ) + } } diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt new file mode 100644 index 000000000..e6a1c15c2 --- /dev/null +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionGroupUsageDetailsUseCaseTest.kt @@ -0,0 +1,947 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.tests.mocking.permission.domain.usecase + +import android.app.AppOpsManager +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.UserHandle +import android.permission.flags.Flags +import android.platform.test.annotations.RequiresFlagsEnabled +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionTimelineUsageModelWrapper +import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase +import com.android.permissioncontroller.permission.domain.usecase.v31.TELECOM_PACKAGE +import com.android.permissioncontroller.permission.utils.LocationUtils +import com.android.permissioncontroller.pm.data.model.v31.PackageAttributionModel +import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.tests.mocking.appops.data.repository.FakeAppOpRepository +import com.android.permissioncontroller.tests.mocking.coroutines.collectLastValue +import com.android.permissioncontroller.tests.mocking.permission.data.repository.FakePermissionRepository +import com.android.permissioncontroller.tests.mocking.pm.data.repository.FakePackageRepository +import com.android.permissioncontroller.tests.mocking.role.data.repository.FakeRoleRepository +import com.android.permissioncontroller.tests.mocking.user.data.repository.FakeUserRepository +import com.android.permissioncontroller.user.data.repository.v31.UserRepository +import com.google.common.truth.Truth +import java.util.concurrent.TimeUnit.HOURS +import java.util.concurrent.TimeUnit.MINUTES +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import org.mockito.MockitoSession +import org.mockito.quality.Strictness + +@RunWith(AndroidJUnit4::class) +class GetPermissionGroupUsageDetailsUseCaseTest { + @Mock private lateinit var application: PermissionControllerApplication + @Mock private lateinit var context: Context + + private var mockitoSession: MockitoSession? = null + private lateinit var packageInfos: MutableMap<String, PackageInfoModel> + + private val currentUser = android.os.Process.myUserHandle() + private val privateProfile = UserHandle.of(10) + private val guestUser = UserHandle.of(20) + + private val testPackageName = "test.package" + private val guestUserPkgName = "test.package.guest" + private val exemptedPkgName = "test.exempted.package" + private val systemPackageName = "test.package.system" + + @Before + fun setup() { + Assume.assumeTrue(SdkLevel.isAtLeastS()) + MockitoAnnotations.initMocks(this) + mockitoSession = + ExtendedMockito.mockitoSession() + .mockStatic(PermissionControllerApplication::class.java) + .mockStatic(LocationUtils::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + + whenever(PermissionControllerApplication.get()).thenReturn(application) + whenever(application.applicationContext).thenReturn(context) + whenever(LocationUtils.isLocationProvider(Mockito.any(), Mockito.any())).thenReturn(false) + + packageInfos = + mapOf( + testPackageName to getPackageInfoModel(testPackageName), + guestUserPkgName to getPackageInfoModel(guestUserPkgName), + exemptedPkgName to getPackageInfoModel(exemptedPkgName), + systemPackageName to + getPackageInfoModel( + systemPackageName, + applicationFlags = ApplicationInfo.FLAG_SYSTEM, + ), + ) + .toMutableMap() + } + + @After + fun finish() { + mockitoSession?.finishMocking() + } + + @Test + fun guestUserUsagesAreFiltered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(5), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(guestUserPkgName, guestUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + Truth.assertThat(permissionTimelineUsages.map { it.userId }) + .doesNotContain(guestUser.identifier) + } + + @Test + fun quiteProfileAndShowUsageInQuietMode() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastV()) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(5), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents), + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + userRepository = + FakeUserRepository( + currentUserProfiles = + listOf(currentUser.identifier, privateProfile.identifier), + quietUserProfiles = listOf(privateProfile.identifier), + showInQuiteModeProfiles = listOf(privateProfile.identifier), + ), + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(4) + Truth.assertThat(permissionTimelineUsages.map { it.userId }.toSet()) + .isEqualTo(setOf(currentUser.identifier, privateProfile.identifier)) + } + + @Test + fun quietProfileAndDoNotShowInQuietMode() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastV()) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(5), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents), + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + userRepository = + FakeUserRepository( + currentUserProfiles = + listOf(currentUser.identifier, privateProfile.identifier), + quietUserProfiles = listOf(privateProfile.identifier), + ), + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + Truth.assertThat(permissionTimelineUsages.map { it.userId }) + .contains(currentUser.identifier) + Truth.assertThat(permissionTimelineUsages.map { it.userId }) + .doesNotContain(privateProfile.identifier) + } + + @Test + fun exemptedPackageIsFiltered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(5), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(exemptedPkgName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + val actualPackages = permissionTimelineUsages.map { it.packageName } + Truth.assertThat(actualPackages).contains(testPackageName) + Truth.assertThat(actualPackages).doesNotContain(exemptedPkgName) + } + + @Test + fun packageNoLongerRequestingPermissionIsFiltered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(5), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + // package no more request location permission + packageInfos[testPackageName] = + getPackageInfoModel(testPackageName, requestedPermissions = listOf(CAMERA_PERMISSION)) + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages).isEmpty() + } + + @Test + fun discreteAccessesAreClustered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(2), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(3), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(3)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(3)) + } + + @Test + fun continuousAccessesAreClustered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(1), + MINUTES.toMillis(1), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(2), + MINUTES.toMillis(1), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(3), + MINUTES.toMillis(2), + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(CAMERA_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(4)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(4)) + } + + @Test + fun accessesAreClusteredAcrossHours() = runTest { + val hours3 = HOURS.toMillis(3) + val hours4 = HOURS.toMillis(4) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_FINE_LOCATION, + hours3 + MINUTES.toMillis(59), + -1, + ), + DiscreteOpModel( + AppOpsManager.OPSTR_FINE_LOCATION, + hours4 + MINUTES.toMillis(0), + -1, + ), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, hours4 + MINUTES.toMillis(1), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(hours3 + MINUTES.toMillis(59)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(hours4 + MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(3)) + } + + @Test + fun overlappingDurationAreClustered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(1), + MINUTES.toMillis(3), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(3), + MINUTES.toMillis(2), + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(CAMERA_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(4)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(4)) + } + + @Test + fun discreteAccessesAreNotClustered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(3), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + var timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(1)) + + timelineModel = permissionTimelineUsages.last() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(3)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(3)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(1)) + } + + @Test + fun continuousAccessesAreNotClustered() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(1), + MINUTES.toMillis(3), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(5), + MINUTES.toMillis(5), + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(CAMERA_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + var timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(3)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(3)) + + timelineModel = permissionTimelineUsages.last() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(5)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(9)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(5)) + } + + @Test + fun singleDiscreteAccess() = runTest { + val appOpEvents = + listOf(DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1)) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(1)) + } + + @Test + fun singleContinuousAccess() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(1), + MINUTES.toMillis(3), + ) + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(CAMERA_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(3)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(3)) + } + + @Test + fun unexpectedContinuousAccess() = runTest { + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(1), + MINUTES.toMillis(2), + ), + // This entry says the camera was accessed for 15 minutes starting at minute 3 + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(3), + MINUTES.toMillis(15), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(4), + MINUTES.toMillis(1), + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + MINUTES.toMillis(6), + MINUTES.toMillis(1), + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(CAMERA_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + val timelineModel = permissionTimelineUsages.first() + Truth.assertThat(timelineModel.accessStartMillis).isEqualTo(MINUTES.toMillis(1)) + Truth.assertThat(timelineModel.accessEndMillis).isEqualTo(MINUTES.toMillis(17)) + Truth.assertThat(timelineModel.durationMillis).isEqualTo(MINUTES.toMillis(17)) + } + + @Test + fun verifyUserSensitiveFlags() = runTest { + val appOpEvents = + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1)) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + val permissionFlags = + mapOf<String, Int>( + COARSE_LOCATION_PERMISSION to + PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + permissionFlags = permissionFlags, + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + val packageModelsMap = permissionTimelineUsages.groupBy { it.packageName } + val systemPackageModels = packageModelsMap[systemPackageName] + Truth.assertThat(systemPackageModels?.size).isEqualTo(1) + Truth.assertThat(systemPackageModels?.first()?.isUserSensitive).isEqualTo(true) + val testPackageModels = packageModelsMap[testPackageName] + Truth.assertThat(testPackageModels?.size).isEqualTo(1) + Truth.assertThat(testPackageModels?.first()?.isUserSensitive).isEqualTo(true) + } + + @Test + fun verifyNotUserSensitiveFlagsForSystemPackage() = runTest { + val appOpEvents = + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1)) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + val permissionFlags = + mapOf<String, Int>( + // 0 represents not user sensitive + COARSE_LOCATION_PERMISSION to 0 + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + permissionFlags = permissionFlags, + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + Truth.assertThat(permissionTimelineUsages.first().isUserSensitive).isEqualTo(false) + } + + @Test + fun verifyCameraUserSensitiveFlagsForTelecomPackage() = runTest { + val appOpEvents = + listOf(DiscreteOpModel(AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), -1)) + packageInfos[TELECOM_PACKAGE] = getPackageInfoModel(TELECOM_PACKAGE) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents) + ) + ) + } + val permissionFlags = + mapOf<String, Int>( + CAMERA_PERMISSION to PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + CAMERA_PERMISSION_GROUP, + discretePackageOps, + permissionFlags = permissionFlags, + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + Truth.assertThat(permissionTimelineUsages.first().packageName).isEqualTo(TELECOM_PACKAGE) + Truth.assertThat(permissionTimelineUsages.first().isUserSensitive).isEqualTo(false) + } + + @Test + fun verifyLocationUserSensitiveFlagsForTelecomPackage() = runTest { + val appOpEvents = + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1)) + packageInfos[TELECOM_PACKAGE] = getPackageInfoModel(TELECOM_PACKAGE) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents) + ) + ) + } + val permissionFlags = + mapOf<String, Int>( + COARSE_LOCATION_PERMISSION to + PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + permissionFlags = permissionFlags, + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + Truth.assertThat(permissionTimelineUsages.first().packageName).isEqualTo(TELECOM_PACKAGE) + Truth.assertThat(permissionTimelineUsages.first().isUserSensitive).isEqualTo(true) + } + + @Test + @RequiresFlagsEnabled( + com.android.permission.flags.Flags.FLAG_PERMISSION_TIMELINE_ATTRIBUTION_LABEL_FIX + ) + @Ignore("b/365004787") + fun verifyAccessIsNotGroupedByAttributionLabelAndClustered() = runTest { + // The package is not a location provider. + val appOpEvents = + listOf( + // These entries should be clustered even though they have + // different attribution labels. + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(1), + -1, + "tag1", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(2), + -1, + "tag1", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(3), + -1, + "tag3", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(4), + -1, + "tag2", + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + val packageAttributions = mutableMapOf<String, PackageAttributionModel>() + // tag1 and tag3 refers to same attribution label. + val attributionTagToLabelRes = mapOf("tag1" to 100, "tag2" to 200, "tag3" to 100) + val attributionsMap = mapOf(100 to "Tag1 Label", 200 to "Tag2 Label") + packageAttributions[testPackageName] = + PackageAttributionModel( + testPackageName, + true, + attributionTagToLabelRes, + attributionsMap, + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + packageRepository = FakePackageRepository(packageInfos, packageAttributions), + attributionLabelFix = true, + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + } + + @Test + fun verifyAccessIsGroupedByAttributionLabelAndClustered() = runTest { + whenever(LocationUtils.isLocationProvider(Mockito.any(), Mockito.any())).thenReturn(true) + val appOpEvents = + listOf( + // These 3 entries should be grouped and clustered. + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(1), + -1, + "tag1", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(2), + -1, + "tag1", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(3), + -1, + "tag3", + ), + // The access at minute 4 should not be grouped or clustered. + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(4), + -1, + "tag2", + ), + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + MINUTES.toMillis(8), + -1, + "tag2", + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + val packageAttributions = mutableMapOf<String, PackageAttributionModel>() + // tag1 and tag3 refers to same attribution label. + val attributionTagToLabelRes = mapOf("tag1" to 100, "tag2" to 200, "tag3" to 100) + val attributionsMap = mapOf(100 to "Tag1 Label", 200 to "Tag2 Label") + packageAttributions[testPackageName] = + PackageAttributionModel( + testPackageName, + true, + attributionTagToLabelRes, + attributionsMap, + ) + + val underTest = + getPermissionGroupUsageDetailsUseCase( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + packageRepository = FakePackageRepository(packageInfos, packageAttributions), + ) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(3) + val labelToTimelineModelsMap = permissionTimelineUsages.groupBy { it.attributionLabel } + val tag1LabelModels = labelToTimelineModelsMap["Tag1 Label"] + val tag2LabelModels = labelToTimelineModelsMap["Tag2 Label"] + + Truth.assertThat(tag1LabelModels?.size).isEqualTo(1) + Truth.assertThat(tag1LabelModels?.first()?.attributionLabel).isEqualTo("Tag1 Label") + Truth.assertThat(tag1LabelModels?.first()?.attributionTags).isEqualTo(setOf("tag1", "tag3")) + + Truth.assertThat(tag2LabelModels?.size).isEqualTo(2) + Truth.assertThat(tag2LabelModels?.first()?.attributionLabel).isEqualTo("Tag2 Label") + Truth.assertThat(tag2LabelModels?.first()?.attributionTags).isEqualTo(setOf("tag2")) + Truth.assertThat(tag2LabelModels?.last()?.attributionLabel).isEqualTo("Tag2 Label") + Truth.assertThat(tag2LabelModels?.last()?.attributionTags).isEqualTo(setOf("tag2")) + } + + @Test + @Ignore("b/365004787") + @RequiresFlagsEnabled(Flags.FLAG_LOCATION_BYPASS_PRIVACY_DASHBOARD_ENABLED) + fun emergencyAccessesAreNotClusteredWithRegularAccesses() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastV()) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(2), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(2) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_LOCATION_BYPASS_PRIVACY_DASHBOARD_ENABLED) + fun emergencyAccessesAreClustered() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastV()) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, MINUTES.toMillis(1), -1), + DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, MINUTES.toMillis(2), -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) + ) + ) + } + + val underTest = + getPermissionGroupUsageDetailsUseCase(LOCATION_PERMISSION_GROUP, discretePackageOps) + val permissionTimelineUsages = getResult(underTest, this) + + Truth.assertThat(permissionTimelineUsages.size).isEqualTo(1) + } + + private fun TestScope.getResult( + useCase: GetPermissionGroupUsageDetailsUseCase, + coroutineScope: CoroutineScope, + ): List<PermissionTimelineUsageModel> { + val usages by collectLastValue(useCase(coroutineScope)) + return (usages as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels + } + + private fun getPermissionGroupUsageDetailsUseCase( + permissionGroup: String, + discreteUsageFlow: Flow<List<DiscretePackageOpsModel>>, + permissionFlags: Map<String, Int> = emptyMap(), + userRepository: UserRepository = FakeUserRepository(listOf(currentUser.identifier)), + packageRepository: PackageRepository = FakePackageRepository(packageInfos), + attributionLabelFix: Boolean = false, + ): GetPermissionGroupUsageDetailsUseCase { + val permissionRepository = FakePermissionRepository(permissionFlags) + val appOpUsageRepository = FakeAppOpRepository(emptyFlow(), discreteUsageFlow) + val roleRepository = FakeRoleRepository(setOf(exemptedPkgName)) + return GetPermissionGroupUsageDetailsUseCase( + permissionGroup, + packageRepository, + permissionRepository, + appOpUsageRepository, + roleRepository, + userRepository, + attributionLabelFix, + ) + } + + private fun getPackageInfoModel( + packageName: String, + requestedPermissions: List<String> = + listOf(COARSE_LOCATION_PERMISSION, FINE_LOCATION_PERMISSION, CAMERA_PERMISSION), + permissionsFlags: List<Int> = + listOf( + PackageInfo.REQUESTED_PERMISSION_GRANTED, + PackageInfo.REQUESTED_PERMISSION_GRANTED, + PackageInfo.REQUESTED_PERMISSION_GRANTED, + ), + applicationFlags: Int = 0, + ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags) + + companion object { + private val FINE_LOCATION_PERMISSION = android.Manifest.permission.ACCESS_FINE_LOCATION + private val COARSE_LOCATION_PERMISSION = android.Manifest.permission.ACCESS_COARSE_LOCATION + private val CAMERA_PERMISSION = android.Manifest.permission.CAMERA + + private val LOCATION_PERMISSION_GROUP = android.Manifest.permission_group.LOCATION + private val CAMERA_PERMISSION_GROUP = android.Manifest.permission_group.CAMERA + } +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt index d205989d6..aa748b992 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt @@ -29,6 +29,7 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel +import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModelWrapper import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageUseCase import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel @@ -43,6 +44,7 @@ import com.android.permissioncontroller.user.data.repository.v31.UserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Assume @@ -119,7 +121,7 @@ class GetPermissionUsageUseCaseTest { } val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo(listOf(PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true))) } @@ -149,7 +151,7 @@ class GetPermissionUsageUseCaseTest { val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow, userRepo = userRepository) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo( listOf( @@ -181,7 +183,7 @@ class GetPermissionUsageUseCaseTest { ) val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow, userRepo = userRepository) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages).isEmpty() } @@ -209,7 +211,7 @@ class GetPermissionUsageUseCaseTest { } val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo(listOf(PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true))) } @@ -235,7 +237,7 @@ class GetPermissionUsageUseCaseTest { ) val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo(listOf(PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 100, true))) } @@ -258,7 +260,7 @@ class GetPermissionUsageUseCaseTest { } val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo( listOf( @@ -285,7 +287,7 @@ class GetPermissionUsageUseCaseTest { // test package is not a system package val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo( listOf( @@ -328,7 +330,7 @@ class GetPermissionUsageUseCaseTest { val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow, permissionFlags = permissionFlags) - val permissionGroupUsages by collectLastValue(underTest()) + val permissionGroupUsages = getResult(underTest) assertThat(permissionGroupUsages) .isEqualTo( listOf( @@ -338,6 +340,13 @@ class GetPermissionUsageUseCaseTest { ) } + private fun TestScope.getResult( + useCase: GetPermissionGroupUsageUseCase + ): List<PermissionGroupUsageModel> { + val result by collectLastValue(useCase()) + return (result as PermissionGroupUsageModelWrapper.Success).permissionUsageModels + } + private fun getPackageInfoModel( packageName: String, requestedPermissions: List<String> = listOf(CAMERA_PERMISSION, RECORD_AUDIO_PERMISSION), diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt new file mode 100644 index 000000000..edaea9aba --- /dev/null +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageDetailsViewModelTest.kt @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.tests.mocking.permission.ui.model + +import android.app.AppOpsManager +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.permission.flags.Flags +import androidx.lifecycle.SavedStateHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel +import com.android.permissioncontroller.appops.data.model.v31.DiscretePackageOpsModel.DiscreteOpModel +import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageDetailsUseCase +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelV2 +import com.android.permissioncontroller.permission.utils.LocationUtils +import com.android.permissioncontroller.permission.utils.StringUtils +import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.tests.mocking.appops.data.repository.FakeAppOpRepository +import com.android.permissioncontroller.tests.mocking.coroutines.collectLastValue +import com.android.permissioncontroller.tests.mocking.permission.data.repository.FakePermissionRepository +import com.android.permissioncontroller.tests.mocking.pm.data.repository.FakePackageRepository +import com.android.permissioncontroller.tests.mocking.role.data.repository.FakeRoleRepository +import com.android.permissioncontroller.tests.mocking.user.data.repository.FakeUserRepository +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.TimeUnit +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import org.mockito.MockitoSession +import org.mockito.quality.Strictness + +/** + * These unit tests are for new permission timeline implementation, the new view model class is + * [PermissionUsageDetailsViewModelV2] + */ +@RunWith(AndroidJUnit4::class) +class PermissionUsageDetailsViewModelTest { + @Mock private lateinit var application: PermissionControllerApplication + @Mock private lateinit var context: Context + private var mockitoSession: MockitoSession? = null + + private lateinit var packageRepository: PackageRepository + + private val currentUser = android.os.Process.myUserHandle() + private val testPackageName = "test.package" + private val testPackageLabel = "Test Package Label" + private val systemPackageName = "test.package.system" + private lateinit var packageInfos: MutableMap<String, PackageInfoModel> + + @Before + fun setup() { + Assume.assumeTrue(SdkLevel.isAtLeastS()) + MockitoAnnotations.initMocks(this) + mockitoSession = + ExtendedMockito.mockitoSession() + .mockStatic(PermissionControllerApplication::class.java) + .mockStatic(Utils::class.java) + .mockStatic(DeviceUtils::class.java) + .mockStatic(StringUtils::class.java) + .mockStatic(Flags::class.java) + .mockStatic(LocationUtils::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + + whenever(PermissionControllerApplication.get()).thenReturn(application) + whenever(application.applicationContext).thenReturn(context) + whenever(Utils.getUserContext(application, currentUser)).thenReturn(context) + whenever(DeviceUtils.isHandheld()).thenReturn(true) + whenever( + StringUtils.getIcuPluralsString( + any(), + anyInt(), + anyInt(), + any(Array<String>::class.java) + ) + ) + .thenReturn("Duration Summary") + whenever(LocationUtils.isLocationProvider(any(), any())).thenReturn(false) + + packageInfos = + mapOf( + testPackageName to getPackageInfoModel(testPackageName), + systemPackageName to + getPackageInfoModel( + systemPackageName, + applicationFlags = ApplicationInfo.FLAG_SYSTEM + ), + ) + .toMutableMap() + + packageRepository = + FakePackageRepository( + packageInfos, + packagesAndLabels = mapOf(testPackageName to testPackageLabel) + ) + } + + @After + fun finish() { + mockitoSession?.finishMocking() + } + + @Test + fun verifyOnlyNonSystemAppsAreShown() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, accessTimeMillis, -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + savedStateMap = mapOf("show7Days" to false, "showSystem" to false), + ) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.containsSystemAppUsage).isEqualTo(true) + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + assertThat(uiState.appPermissionAccessUiInfoList.first().packageName) + .isEqualTo(testPackageName) + assertThat(uiState.appPermissionAccessUiInfoList.first().packageLabel) + .isEqualTo(testPackageLabel) + } + + @Test + fun verifySystemAppsAreShown() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + savedStateMap = mapOf("show7Days" to false, "showSystem" to true) + ) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.containsSystemAppUsage).isEqualTo(true) + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(2) + assertThat(uiState.appPermissionAccessUiInfoList.map { it.packageName }.toSet()) + .isEqualTo(setOf(testPackageName, systemPackageName)) + } + + @Test + fun verifyNoSystemAppsAvailable() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + savedStateMap = mapOf("show7Days" to false, "showSystem" to false) + ) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.containsSystemAppUsage).isEqualTo(false) + } + + @Test + fun verifyNoSystemAppsAvailableInLast24Hours() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.DAYS.toMillis(2)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + savedStateMap = mapOf("show7Days" to false, "showSystem" to false) + ) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.containsSystemAppUsage).isEqualTo(false) + } + + @Test + fun verify24HoursDataIsShown() = runTest { + val accessStartWithIn24Hours = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val accessStartBefore24Hours = (getCurrentTime() - TimeUnit.HOURS.toMillis(30)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessStartWithIn24Hours, + TimeUnit.MINUTES.toMillis(5) + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessStartBefore24Hours, + TimeUnit.MINUTES.toMillis(7) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = getViewModel(CAMERA_PERMISSION_GROUP, discretePackageOps) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + val timelineRow = uiState.appPermissionAccessUiInfoList.first() + assertThat(timelineRow.accessStartTime).isEqualTo(accessStartWithIn24Hours) + } + + @Test + fun verify7DaysDataIsShown() = runTest { + val accessTimeWithIn24Hours = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val accessTimeBefore24Hours = (getCurrentTime() - TimeUnit.DAYS.toMillis(3)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessTimeWithIn24Hours, + TimeUnit.MINUTES.toMillis(5) + ), + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessTimeBefore24Hours, + TimeUnit.MINUTES.toMillis(7) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + CAMERA_PERMISSION_GROUP, + discretePackageOps, + savedStateMap = mapOf("show7Days" to true, "showSystem" to false), + ) + + val uiState = getPermissionUsageDetailsUiState(underTest) + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(2) + // assert rows are sorted by access timestamps i.e. most recent entry first + assertThat(uiState.appPermissionAccessUiInfoList.map { it.accessStartTime }) + .isInOrder(Comparator.reverseOrder<Long>()) + val lastRow = uiState.appPermissionAccessUiInfoList.last() + assertThat(lastRow.accessStartTime).isEqualTo(accessTimeBefore24Hours) + } + + @Test + fun verifyDurationLabelIsShown() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(5) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = getViewModel(CAMERA_PERMISSION_GROUP, discretePackageOps) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + val timelineRow = uiState.appPermissionAccessUiInfoList.first() + assertThat(timelineRow.summaryText).isEqualTo("Duration Summary") + } + + @Test + fun verifyDurationLabelIsNotShown() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_CAMERA, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = getViewModel(CAMERA_PERMISSION_GROUP, discretePackageOps) + val uiState = getPermissionUsageDetailsUiState(underTest) + + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + val timelineRow = uiState.appPermissionAccessUiInfoList.first() + assertThat(timelineRow.summaryText).isNull() + } + + @Test + fun verifyEmergencyLocationAccessWithAttribution() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastV()) + whenever(Flags.locationBypassPrivacyDashboardEnabled()).thenReturn(true) + whenever(application.getString(anyInt())).thenReturn("emergency attr label") + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel(AppOpsManager.OPSTR_EMERGENCY_LOCATION, accessTimeMillis, -1), + ) + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + ) + val uiState = getPermissionUsageDetailsUiState(underTest) + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + val timelineRow = uiState.appPermissionAccessUiInfoList.first() + assertThat(timelineRow.isEmergencyLocationAccess).isTrue() + assertThat(timelineRow.showingAttribution).isTrue() + assertThat(timelineRow.summaryText).isEqualTo("emergency attr label") + } + + @Test + fun verifyNewEmittedEventsAreCollected() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(5)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + val actualData = + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + val discretePackageOps = MutableStateFlow(emptyList<DiscretePackageOpsModel>()) + discretePackageOps.emit(emptyList()) + + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + ) + val result by collectLastValue(underTest.permissionUsageDetailsUiStateFlow) + var uiState = result as PermissionUsageDetailsUiState.Success + assertThat(uiState.appPermissionAccessUiInfoList).isEmpty() + + // verify that the emitted value is collected + discretePackageOps.emit(actualData) + val updatedResult by collectLastValue(underTest.permissionUsageDetailsUiStateFlow) + uiState = updatedResult as PermissionUsageDetailsUiState.Success + assertThat(uiState.appPermissionAccessUiInfoList.size).isEqualTo(1) + } + + @Test + fun verifyObserverIsNotifiedOnUserActionWhenDataIsSame() = runTest { + val accessTimeMillis = (getCurrentTime() - TimeUnit.HOURS.toMillis(2)) + val appOpEvents = + listOf( + DiscreteOpModel( + AppOpsManager.OPSTR_COARSE_LOCATION, + accessTimeMillis, + TimeUnit.MINUTES.toMillis(1) + ), + ) + + val discretePackageOps = flow { + emit( + listOf( + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + ) + ) + } + val underTest = + getViewModel( + LOCATION_PERMISSION_GROUP, + discretePackageOps, + ) + + val uiState = getPermissionUsageDetailsUiState(underTest) + assertThat(uiState.show7Days).isFalse() + + // perform user action + underTest.updateShow7DaysToggle(true) + val uiState2 = getPermissionUsageDetailsUiState(underTest) + assertThat(uiState2.show7Days).isTrue() + } + + private fun getPermissionGroupUsageDetailsUseCase( + permissionGroup: String, + discreteUsageFlow: Flow<List<DiscretePackageOpsModel>>, + ): GetPermissionGroupUsageDetailsUseCase { + val userRepository = FakeUserRepository(listOf(currentUser.identifier)) + val permissionRepository = FakePermissionRepository() + val appOpUsageRepository = FakeAppOpRepository(emptyFlow(), discreteUsageFlow) + val roleRepository = FakeRoleRepository() + return GetPermissionGroupUsageDetailsUseCase( + permissionGroup, + packageRepository, + permissionRepository, + appOpUsageRepository, + roleRepository, + userRepository, + false, + ) + } + + /** Get current time rounded at minute level. */ + private fun getCurrentTime(): Long { + return System.currentTimeMillis() / ONE_MINUTE_MILLIS * ONE_MINUTE_MILLIS + } + + private fun TestScope.getViewModel( + permissionGroup: String, + discretePackageOps: Flow<List<DiscretePackageOpsModel>>, + savedStateMap: Map<String, Boolean> = mapOf("show7Days" to false, "showSystem" to false), + pkgRepository: PackageRepository = packageRepository + ) = + PermissionUsageDetailsViewModelV2( + application, + getPermissionGroupUsageDetailsUseCase(permissionGroup, discretePackageOps), + SavedStateHandle(savedStateMap), + permissionGroup, + scope = backgroundScope, + StandardTestDispatcher(testScheduler), + packageRepository = pkgRepository + ) + + private fun TestScope.getPermissionUsageDetailsUiState( + viewModel: PermissionUsageDetailsViewModelV2 + ): PermissionUsageDetailsUiState.Success { + val result by collectLastValue(viewModel.permissionUsageDetailsUiStateFlow) + return result as PermissionUsageDetailsUiState.Success + } + + private fun getPackageInfoModel( + packageName: String, + requestedPermissions: List<String> = listOf(CAMERA_PERMISSION, COARSE_LOCATION_PERMISSION), + permissionsFlags: List<Int> = + listOf( + PackageInfo.REQUESTED_PERMISSION_GRANTED, + PackageInfo.REQUESTED_PERMISSION_GRANTED + ), + applicationFlags: Int = 0, + ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags) + + companion object { + private const val ONE_MINUTE_MILLIS = 60_000 + private val COARSE_LOCATION_PERMISSION = android.Manifest.permission.ACCESS_COARSE_LOCATION + private val CAMERA_PERMISSION = android.Manifest.permission.CAMERA + + private val LOCATION_PERMISSION_GROUP = android.Manifest.permission_group.LOCATION + private val CAMERA_PERMISSION_GROUP = android.Manifest.permission_group.CAMERA + } +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt index 38390ca64..cf18cdf7d 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt @@ -21,9 +21,11 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager +import androidx.lifecycle.SavedStateHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.DeviceUtils import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel @@ -43,6 +45,7 @@ import com.google.common.truth.Truth.assertThat import java.util.concurrent.TimeUnit import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Assume @@ -66,6 +69,7 @@ class PermissionUsageViewModelTest { private val testPackageName = "test.package" private val systemPackageName = "test.package.system" private lateinit var packageInfos: MutableMap<String, PackageInfoModel> + @Before fun setup() { Assume.assumeTrue(SdkLevel.isAtLeastS()) @@ -73,11 +77,13 @@ class PermissionUsageViewModelTest { mockitoSession = ExtendedMockito.mockitoSession() .mockStatic(PermissionControllerApplication::class.java) + .mockStatic(DeviceUtils::class.java) .strictness(Strictness.LENIENT) .startMocking() whenever(PermissionControllerApplication.get()).thenReturn(application) whenever(application.applicationContext).thenReturn(context) + whenever(DeviceUtils.isHandheld()).thenReturn(true) PermissionMapping.addHealthPermissionsToPlatform(setOf("health1")) val permissionFlags = @@ -105,21 +111,8 @@ class PermissionUsageViewModelTest { @Test fun allPermissionGroupsAreShown() = runTest { - val permissionUsageViewModel = - PermissionUsageViewModel( - application, - permissionRepository, - getPermissionGroupUsageUseCase(), - backgroundScope, - StandardTestDispatcher(testScheduler), - is7DayToggleEnabled = true - ) - - val uiData = - checkNotNull( - collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke() - ) - as PermissionUsagesUiState.Success + val permissionUsageViewModel = getViewModel() + val uiData = getPermissionUsageUiState(permissionUsageViewModel) val expectedPermissions = PermissionMapping.getPlatformPermissionGroups().toMutableSet() if (SdkLevel.isAtLeastT()) { @@ -129,7 +122,7 @@ class PermissionUsageViewModelTest { } @Test - fun permissionGroupsCountNonSystemApps() = runTest { + fun onlyNonSystemAppsUsageIsCounted() = runTest { val timestamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5) val appOpsUsage = listOf( @@ -143,26 +136,19 @@ class PermissionUsageViewModelTest { ) val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels) val permissionUsageViewModel = - PermissionUsageViewModel( - application, - permissionRepository, - permissionUsageUseCase, - backgroundScope, - StandardTestDispatcher(testScheduler), - is7DayToggleEnabled = true - ) - val uiData = - checkNotNull( - collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke() + getViewModel( + useCase = permissionUsageUseCase, + savedStateHandle = SavedStateHandle(mapOf("showSystem" to false)) ) - as PermissionUsagesUiState.Success + val uiData = getPermissionUsageUiState(permissionUsageViewModel) + val permissionGroupsCount = uiData.permissionGroupUsageCount assertThat(permissionGroupsCount[CAMERA_PERMISSION_GROUP]).isEqualTo(2) assertThat(permissionGroupsCount[MICROPHONE_PERMISSION_GROUP]).isEqualTo(1) } @Test - fun permissionGroupsCountAllApps() = runTest { + fun systemAppsUsageIsCounted() = runTest { val timestamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5) val appOpsUsage = listOf( @@ -176,23 +162,111 @@ class PermissionUsageViewModelTest { ) val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels) val permissionUsageViewModel = - PermissionUsageViewModel( - application, - permissionRepository, - permissionUsageUseCase, - backgroundScope, - StandardTestDispatcher(testScheduler), - is7DayToggleEnabled = true + getViewModel( + useCase = permissionUsageUseCase, + savedStateHandle = SavedStateHandle(mapOf("showSystem" to true)) ) + val uiData = getPermissionUsageUiState(permissionUsageViewModel) - collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke() - val uiData = - permissionUsageViewModel.updateShowSystem(true) as PermissionUsagesUiState.Success val permissionGroupsCount = uiData.permissionGroupUsageCount assertThat(permissionGroupsCount[CAMERA_PERMISSION_GROUP]).isEqualTo(2) assertThat(permissionGroupsCount[MICROPHONE_PERMISSION_GROUP]).isEqualTo(2) } + @Test + fun noSystemAppsAvailableInLast24Hours() = runTest { + val timestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2) + val appOpsUsage = + listOf( + AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, timestamp), + AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, timestamp), + ) + val appOpsUsageModels = + listOf( + PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier), + PackageAppOpUsageModel(systemPackageName, appOpsUsage, currentUser.identifier), + ) + val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels) + val permissionUsageViewModel = getViewModel(useCase = permissionUsageUseCase) + val uiData = getPermissionUsageUiState(permissionUsageViewModel) + + assertThat(uiData.containsSystemAppUsage).isFalse() + } + + @Test + fun appUsageIsCountedForLast7Days() = runTest { + val timestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2) + val appOpsUsage = + listOf( + AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, timestamp), + AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, timestamp), + ) + val appOpsUsageModels = + listOf( + PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier), + ) + val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels) + val permissionUsageViewModel = + getViewModel( + useCase = permissionUsageUseCase, + savedStateHandle = SavedStateHandle(mapOf("show7Days" to true)) + ) + val permissionGroupsCount = + getPermissionUsageUiState(permissionUsageViewModel).permissionGroupUsageCount + + assertThat(permissionGroupsCount[CAMERA_PERMISSION_GROUP]).isEqualTo(1) + assertThat(permissionGroupsCount[MICROPHONE_PERMISSION_GROUP]).isEqualTo(1) + } + + @Test + fun verifyObserverIsNotifiedOnUserActionWhenDataIsSame() = runTest { + val timestamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2) + val appOpsUsage = + listOf( + AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, timestamp), + AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, timestamp), + ) + val appOpsUsageModels = + listOf( + PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier), + ) + val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels) + val permissionUsageViewModel = + getViewModel( + useCase = permissionUsageUseCase, + savedStateHandle = SavedStateHandle(mapOf("show7Days" to false)) + ) + + val uiState = getPermissionUsageUiState(permissionUsageViewModel) + assertThat(uiState.show7Days).isFalse() + + // perform user action + permissionUsageViewModel.updateShow7Days(true) + val uiState2 = getPermissionUsageUiState(permissionUsageViewModel) + assertThat(uiState2.show7Days).isTrue() + } + + private fun TestScope.getViewModel( + useCase: GetPermissionGroupUsageUseCase = getPermissionGroupUsageUseCase(), + savedStateHandle: SavedStateHandle = SavedStateHandle(emptyMap()) + ): PermissionUsageViewModel { + return PermissionUsageViewModel( + application, + permissionRepository, + useCase, + backgroundScope, + StandardTestDispatcher(testScheduler), + savedState = savedStateHandle + ) + } + + private fun TestScope.getPermissionUsageUiState( + viewModel: PermissionUsageViewModel + ): PermissionUsagesUiState.Success { + val result by collectLastValue(viewModel.permissionUsagesUiDataFlow) + return result as PermissionUsagesUiState.Success + } + private fun getPermissionGroupUsageUseCase( packageAppOpsUsages: List<PackageAppOpUsageModel> = emptyList(), ): GetPermissionGroupUsageUseCase { 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 899a026c4..0e8890df5 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 @@ -104,7 +104,7 @@ class ReviewPermissionsViewModelTest { permissionsMap["mockedPermission1"] = permission2 whenever(permGroup.allPermissions).thenReturn(permissionsMap) - whenever(permission1.isGrantedIncludingAppOp).thenReturn(true) + whenever(permission1.isGranted).thenReturn(true) val summary = model.getSummaryForIndividuallyControlledPermGroup(permGroup) assertEquals( diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt index 8bd61ebe6..28f69b136 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt @@ -25,6 +25,7 @@ import android.app.AppOpsManager.MODE_IGNORED import android.app.AppOpsManager.permissionToOp import android.app.Application import android.content.Context +import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED import android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME @@ -216,7 +217,8 @@ class GrantRevokeTests { backgroundPerm, PermissionInfo.PROTECTION_DANGEROUS, permInfoProtectionFlags, - 0 + 0, + pkgInfo.appFlags and ApplicationInfo.FLAG_SYSTEM != 0 ) return LightPermission( pkgInfo, @@ -292,7 +294,7 @@ class GrantRevokeTests { val flags = state.second assertWithMessage("permission $permName grant state incorrect") - .that(perms[permName]?.isGrantedIncludingAppOp) + .that(perms[permName]?.isGranted) .isEqualTo(granted) val actualFlags = perms[permName]!!.flags diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt index 29557b0b2..d199818c1 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt @@ -16,12 +16,25 @@ package com.android.permissioncontroller.tests.mocking.pm.data.repository +import android.graphics.drawable.Drawable import android.os.UserHandle +import com.android.permissioncontroller.pm.data.model.v31.PackageAttributionModel import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository -class FakePackageRepository(private val packages: Map<String, PackageInfoModel> = emptyMap()) : - PackageRepository { +class FakePackageRepository( + private val packages: Map<String, PackageInfoModel> = emptyMap(), + private val packageAttributions: Map<String, PackageAttributionModel> = emptyMap(), + private val packagesAndLabels: Map<String, String> = emptyMap(), +) : PackageRepository { + override fun getPackageLabel(packageName: String, user: UserHandle): String { + return packagesAndLabels[packageName] ?: packageName + } + + override fun getBadgedPackageIcon(packageName: String, user: UserHandle): Drawable? { + return null + } + override suspend fun getPackageInfo( packageName: String, user: UserHandle, @@ -29,4 +42,11 @@ class FakePackageRepository(private val packages: Map<String, PackageInfoModel> ): PackageInfoModel? { return packages[packageName] } + + override suspend fun getPackageAttributionInfo( + packageName: String, + user: UserHandle + ): PackageAttributionModel? { + return packageAttributions[packageName] + } } diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/PackageRepositoryTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/PackageRepositoryTest.kt new file mode 100644 index 000000000..257cc2ac3 --- /dev/null +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/PackageRepositoryTest.kt @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.tests.mocking.pm.data.repository + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.Attribution +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.modules.utils.build.SdkLevel +import com.android.permissioncontroller.PermissionControllerApplication +import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository +import com.android.permissioncontroller.pm.data.repository.v31.PackageRepositoryImpl +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.eq +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import org.mockito.MockitoSession +import org.mockito.quality.Strictness + +@RunWith(AndroidJUnit4::class) +class PackageRepositoryTest { + @Mock private lateinit var application: PermissionControllerApplication + + @Mock private lateinit var context: Context + + @Mock private lateinit var packageManager: PackageManager + + private lateinit var underTest: PackageRepository + private var mockitoSession: MockitoSession? = null + + private val currentUser = android.os.Process.myUserHandle() + private val testPackageName = "test.package" + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + mockitoSession = + ExtendedMockito.mockitoSession() + .mockStatic(PermissionControllerApplication::class.java) + .mockStatic(Utils::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + whenever(PermissionControllerApplication.get()).thenReturn(application) + whenever(Utils.getUserContext(application, currentUser)).thenReturn(context) + whenever(context.packageManager).thenReturn(packageManager) + + underTest = PackageRepositoryImpl(application) + } + + @After + fun finish() { + mockitoSession?.finishMocking() + } + + @Test + @Suppress("DEPRECATION") + fun verifyMissingPackageAttributionInfo() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastS()) + val mockData = getPackageInfoWithoutAttribution() + whenever( + packageManager.getPackageInfo( + eq(testPackageName), + eq(PackageManager.GET_ATTRIBUTIONS) + ) + ) + .thenReturn(mockData) + + val attributionInfo = underTest.getPackageAttributionInfo(testPackageName, currentUser) + assertThat(attributionInfo).isNotNull() + assertThat(attributionInfo?.packageName).isEqualTo(testPackageName) + assertThat(attributionInfo?.areUserVisible).isEqualTo(false) + assertThat(attributionInfo?.tagResourceMap).isNull() + assertThat(attributionInfo?.resourceLabelMap).isNull() + } + + @Test + @Suppress("DEPRECATION") + fun verifyPackageAttributionInfo() = runTest { + Assume.assumeTrue(SdkLevel.isAtLeastS()) + val mockData = getPackageInfoWithAttribution() + whenever(application.createPackageContext(eq(testPackageName), eq(0))).thenReturn(context) + whenever(context.getString(eq(100))).thenReturn("tag1 Label") + whenever( + packageManager.getPackageInfo( + eq(testPackageName), + eq(PackageManager.GET_ATTRIBUTIONS) + ) + ) + .thenReturn(mockData) + + val expectedAttributionMap = mutableMapOf<Int, String>() + expectedAttributionMap[100] = "tag1 Label" + + val expectedTagToLabelResMap = mutableMapOf<String, Int>() + expectedTagToLabelResMap["tag1"] = 100 + expectedTagToLabelResMap["tag2"] = 200 + val attributionInfo = underTest.getPackageAttributionInfo(testPackageName, currentUser) + assertThat(attributionInfo).isNotNull() + assertThat(attributionInfo?.packageName).isEqualTo(testPackageName) + assertThat(attributionInfo?.areUserVisible).isEqualTo(true) + assertThat(attributionInfo?.tagResourceMap).isEqualTo(expectedTagToLabelResMap) + assertThat(attributionInfo?.resourceLabelMap).isEqualTo(expectedAttributionMap) + } + + private fun getPackageInfoWithoutAttribution(): PackageInfo { + val info = mock(ApplicationInfo::class.java) + whenever(info.areAttributionsUserVisible()).thenReturn(false) + + return PackageInfo().apply { + packageName = testPackageName + applicationInfo = info + requestedPermissions = listOf<String>().toTypedArray() + requestedPermissionsFlags = listOf<Int>().toIntArray() + } + } + + private fun getPackageInfoWithAttribution(): PackageInfo { + val attribution = mock(Attribution::class.java) + whenever(attribution.label).thenReturn(100) + whenever(attribution.tag).thenReturn("tag1") + val attribution2 = mock(Attribution::class.java) + whenever(attribution2.label).thenReturn(200) + whenever(attribution2.tag).thenReturn("tag2") + + val info = mock(ApplicationInfo::class.java) + whenever(info.areAttributionsUserVisible()).thenReturn(true) + + return PackageInfo().apply { + packageName = testPackageName + applicationInfo = info + requestedPermissions = listOf<String>().toTypedArray() + requestedPermissionsFlags = listOf<Int>().toIntArray() + attributions = listOf(attribution, attribution2).toTypedArray() + } + } +} diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt index bc00d3bc8..4bb021b3d 100644 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt +++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/NotificationListenerCheckInternalTest.kt @@ -30,7 +30,6 @@ import android.safetycenter.SafetyCenterManager import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceData import android.safetycenter.SafetySourceIssue -import androidx.core.util.Preconditions import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress @@ -464,9 +463,7 @@ class NotificationListenerCheckInternalTest { } val safetySourceIssue = - Preconditions.checkNotNull( - notificationListenerCheck.createSafetySourceIssue(testComponent, 0) - ) + checkNotNull(notificationListenerCheck.createSafetySourceIssue(testComponent, 0)) val expectedId = "notification_listener_${testComponent.flattenToString()}" val expectedTitle = 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 6a45ac7c4..a0199979d 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 @@ -40,10 +40,10 @@ import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.Refr import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Before @@ -74,7 +74,7 @@ class SafetyCenterReceiverTest { val application = Mockito.mock(PermissionControllerApplication::class.java) } - private val testCoroutineDispatcher = TestCoroutineDispatcher() + private val unconfinedCoroutineDispatcher = UnconfinedTestDispatcher() @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager @Mock lateinit var mockPackageManager: PackageManager @@ -88,7 +88,7 @@ class SafetyCenterReceiverTest { private fun privacySourceMap(context: Context) = mapOf( TEST_PRIVACY_SOURCE_ID to mockPrivacySource, - TEST_PRIVACY_SOURCE_ID_2 to mockPrivacySource2 + TEST_PRIVACY_SOURCE_ID_2 to mockPrivacySource2, ) @Before @@ -110,28 +110,28 @@ class SafetyCenterReceiverTest { whenever( Utils.getSystemServiceSafe( any(ContextWrapper::class.java), - eq(UserManager::class.java) + eq(UserManager::class.java), ) ) .thenReturn(mockUserManager) whenever( Utils.getSystemServiceSafe( any(ContextWrapper::class.java), - eq(SafetyCenterManager::class.java) + eq(SafetyCenterManager::class.java), ) ) .thenReturn(mockSafetyCenterManager) whenever(mockUserManager.isProfile).thenReturn(false) - safetyCenterReceiver = SafetyCenterReceiver(::privacySourceMap, testCoroutineDispatcher) + safetyCenterReceiver = + SafetyCenterReceiver(::privacySourceMap, unconfinedCoroutineDispatcher) - Dispatchers.setMain(testCoroutineDispatcher) + Dispatchers.setMain(unconfinedCoroutineDispatcher) } @After fun cleanup() { Dispatchers.resetMain() - testCoroutineDispatcher.cleanupTestCoroutines() mockitoSession.finishMocking() } @@ -141,91 +141,104 @@ class SafetyCenterReceiverTest { DeviceConfig.getInt( eq(DeviceConfig.NAMESPACE_PRIVACY), eq(SafetyCenterQsTileService.QS_TILE_COMPONENT_SETTING_FLAGS), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .thenReturn(PackageManager.DONT_KILL_APP) } @Test - fun onReceive_actionSafetyCenterEnabledChanged() = runBlockingTest { - mockQSTileSettingsFlag() - safetyCenterReceiver.onReceive(application, Intent(ACTION_SAFETY_CENTER_ENABLED_CHANGED)) + fun onReceive_actionSafetyCenterEnabledChanged() = + runTest(UnconfinedTestDispatcher()) { + mockQSTileSettingsFlag() + safetyCenterReceiver.onReceive( + application, + Intent(ACTION_SAFETY_CENTER_ENABLED_CHANGED), + ) - verify(mockPrivacySource).safetyCenterEnabledChanged(application, true) - verify(mockPrivacySource2).safetyCenterEnabledChanged(application, true) - } + verify(mockPrivacySource).safetyCenterEnabledChanged(application, true) + verify(mockPrivacySource2).safetyCenterEnabledChanged(application, true) + } @Test - fun onReceive_actionSafetyCenterEnabledChanged_safetyCenterDisabled() = runBlockingTest { - mockQSTileSettingsFlag() - whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) - - safetyCenterReceiver.onReceive(application, Intent(ACTION_SAFETY_CENTER_ENABLED_CHANGED)) - advanceUntilIdle() + fun onReceive_actionSafetyCenterEnabledChanged_safetyCenterDisabled() = + runTest(UnconfinedTestDispatcher()) { + mockQSTileSettingsFlag() + whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) + + safetyCenterReceiver.onReceive( + application, + Intent(ACTION_SAFETY_CENTER_ENABLED_CHANGED), + ) + advanceUntilIdle() - verify(mockPrivacySource).safetyCenterEnabledChanged(application, false) - verify(mockPrivacySource2).safetyCenterEnabledChanged(application, false) - } + verify(mockPrivacySource).safetyCenterEnabledChanged(application, false) + verify(mockPrivacySource2).safetyCenterEnabledChanged(application, false) + } @Test - fun onReceive_actionBootCompleted() = runBlockingTest { - val intent = Intent(ACTION_BOOT_COMPLETED) + fun onReceive_actionBootCompleted() = + runTest(UnconfinedTestDispatcher()) { + val intent = Intent(ACTION_BOOT_COMPLETED) - safetyCenterReceiver.onReceive(application, intent) - advanceUntilIdle() + safetyCenterReceiver.onReceive(application, intent) + advanceUntilIdle() - verify(mockPrivacySource) - .rescanAndPushSafetyCenterData(application, intent, EVENT_DEVICE_REBOOTED) - verify(mockPrivacySource2) - .rescanAndPushSafetyCenterData(application, intent, EVENT_DEVICE_REBOOTED) - } + verify(mockPrivacySource) + .rescanAndPushSafetyCenterData(application, intent, EVENT_DEVICE_REBOOTED) + verify(mockPrivacySource2) + .rescanAndPushSafetyCenterData(application, intent, EVENT_DEVICE_REBOOTED) + } @Test - fun onReceive_actionBootCompleted_safetyCenterDisabled() = runBlockingTest { - whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) - val intent = Intent(ACTION_BOOT_COMPLETED) + fun onReceive_actionBootCompleted_safetyCenterDisabled() = + runTest(UnconfinedTestDispatcher()) { + whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) + val intent = Intent(ACTION_BOOT_COMPLETED) - safetyCenterReceiver.onReceive(application, intent) - advanceUntilIdle() + safetyCenterReceiver.onReceive(application, intent) + advanceUntilIdle() - verifyZeroInteractions(mockPrivacySource) - verifyZeroInteractions(mockPrivacySource2) - } + verifyZeroInteractions(mockPrivacySource) + verifyZeroInteractions(mockPrivacySource2) + } @Test - fun onReceive_actionRefreshSafetySources() = runBlockingTest { - val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) - intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, arrayOf(TEST_PRIVACY_SOURCE_ID)) + fun onReceive_actionRefreshSafetySources() = + runTest(UnconfinedTestDispatcher()) { + val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) + intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, arrayOf(TEST_PRIVACY_SOURCE_ID)) - safetyCenterReceiver.onReceive(application, intent) - advanceUntilIdle() + safetyCenterReceiver.onReceive(application, intent) + advanceUntilIdle() - verify(mockPrivacySource) - .rescanAndPushSafetyCenterData(application, intent, EVENT_REFRESH_REQUESTED) - verifyZeroInteractions(mockPrivacySource2) - } + verify(mockPrivacySource) + .rescanAndPushSafetyCenterData(application, intent, EVENT_REFRESH_REQUESTED) + verifyZeroInteractions(mockPrivacySource2) + } @Test - fun onReceive_actionRefreshSafetySources_noSourcesSpecified() = runBlockingTest { - val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) + fun onReceive_actionRefreshSafetySources_noSourcesSpecified() = + runTest(UnconfinedTestDispatcher()) { + val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) - safetyCenterReceiver.onReceive(application, intent) - advanceUntilIdle() + safetyCenterReceiver.onReceive(application, intent) + advanceUntilIdle() - verifyZeroInteractions(mockPrivacySource) - verifyZeroInteractions(mockPrivacySource2) - } + verifyZeroInteractions(mockPrivacySource) + verifyZeroInteractions(mockPrivacySource2) + } @Test - fun onReceive_actionRefreshSafetySources_safetyCenterDisabled() = runBlockingTest { - whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) - val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) + fun onReceive_actionRefreshSafetySources_safetyCenterDisabled() = + runTest(UnconfinedTestDispatcher()) { + whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false) + val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES) - safetyCenterReceiver.onReceive(application, intent) - advanceUntilIdle() + safetyCenterReceiver.onReceive(application, intent) + advanceUntilIdle() - verifyZeroInteractions(mockPrivacySource) - verifyZeroInteractions(mockPrivacySource2) - } + verifyZeroInteractions(mockPrivacySource) + verifyZeroInteractions(mockPrivacySource2) + } } 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 0989903d0..da1430c0a 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 @@ -16,9 +16,17 @@ package com.android.permissioncontroller.tests.mocking.role.model +import android.app.AppOpsManager +import android.content.pm.PackageManager +import android.content.pm.PermissionInfo +import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.permissioncontroller.role.model.RoleParserInitializer +import com.android.role.controller.model.AppOp +import com.android.role.controller.model.Permission +import com.android.role.controller.model.PermissionSet +import com.android.role.controller.model.Role import com.android.role.controller.model.RoleParser import org.junit.BeforeClass import org.junit.Test @@ -34,10 +42,144 @@ class RoleParserTest { } } - private val targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext() + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val uiAutomation = instrumentation.uiAutomation + private val targetContext = instrumentation.targetContext + private val packageManager = targetContext.packageManager @Test - fun testParseRolesWithValidation() { - RoleParser(targetContext, true).parse() + fun parseRoles() { + // We may need to call privileged APIs to determine availability of things. + uiAutomation.adoptShellPermissionIdentity() + try { + RoleParser(targetContext, true).parse() + } finally { + uiAutomation.dropShellPermissionIdentity() + } + } + + @Test + fun validateRoles() { + // We may need to call privileged APIs to determine availability of things. + uiAutomation.adoptShellPermissionIdentity() + try { + val xml = RoleParser(targetContext).parseRolesXml() + requireNotNull(xml) + validateRoles(xml.first, xml.second) + } finally { + uiAutomation.dropShellPermissionIdentity() + } + } + + fun validateRoles(permissionSets: Map<String, PermissionSet>, roles: Map<String, Role>) { + for (permissionSet in permissionSets.values) { + for (permission in permissionSet.permissions) { + validatePermission(permission) + } + } + + for (role in roles.values) { + if (!role.isAvailableByFeatureFlagAndSdkVersion) { + continue + } + + for (requiredComponent in role.requiredComponents) { + val permission = requiredComponent.permission + if (permission != null) { + validatePermission(permission) + } + } + + for (permission in role.permissions) { + validatePermission(permission) + } + + for (appOp in role.appOps) { + validateAppOp(appOp) + } + + for (appOpPermission in role.appOpPermissions) { + validateAppOpPermission(appOpPermission) + } + + for (preferredActivity in role.preferredActivities) { + require(preferredActivity.activity in role.requiredComponents) { + "<activity> of <preferred-activity> not required in <required-components>," + + " role: ${role.name}, preferred activity: $preferredActivity" + } + } + } + } + + private fun validatePermission(permission: Permission) { + if (!permission.isAvailableAsUser(Process.myUserHandle(), targetContext)) { + return + } + validatePermission(permission.name, true) + } + + private fun validatePermission(permissionName: String) { + validatePermission(permissionName, false) + } + + private fun validatePermission(permissionName: String, enforceIsRuntimeOrRole: Boolean) { + val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + // Skip validation for car permissions which may not be available on all build targets. + if (!isAutomotive && permissionName.startsWith("android.car")) { + return + } + + val permissionInfo = + try { + packageManager.getPermissionInfo(permissionName, 0) + } catch (e: PackageManager.NameNotFoundException) { + throw IllegalArgumentException("Unknown permission: $permissionName", e) + } + + if (enforceIsRuntimeOrRole) { + require( + permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS || + permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_ROLE == + PermissionInfo.PROTECTION_FLAG_ROLE + ) { + "Permission is not a runtime or role permission: $permissionName" + } + } + } + + private fun validateAppOpPermission(appOpPermission: Permission) { + if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), targetContext)) { + return + } + validateAppOpPermission(appOpPermission.name) + } + + private fun validateAppOpPermission(permissionName: String) { + val permissionInfo = + try { + packageManager.getPermissionInfo(permissionName, 0) + } catch (e: PackageManager.NameNotFoundException) { + throw IllegalArgumentException("Unknown app op permission: $permissionName", e) + } + require( + permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_APPOP == + PermissionInfo.PROTECTION_FLAG_APPOP + ) { + "Permission is not an app op permission: $permissionName" + } + } + + private fun validateAppOp(appOp: AppOp) { + if (!appOp.isAvailableByFeatureFlagAndSdkVersion) { + return + } + // This throws IllegalArgumentException if app op is unknown. + val permissionName = AppOpsManager.opToPermission(appOp.name) + if (permissionName != null) { + val permissionInfo = packageManager.getPermissionInfo(permissionName, 0) + require(permissionInfo.protection != PermissionInfo.PROTECTION_DANGEROUS) { + "App op has an associated runtime permission: ${appOp.name}" + } + } } } diff --git a/PermissionController/tests/outofprocess/Android.bp b/PermissionController/tests/outofprocess/Android.bp index 83bb65d6b..be0fa90ff 100644 --- a/PermissionController/tests/outofprocess/Android.bp +++ b/PermissionController/tests/outofprocess/Android.bp @@ -37,8 +37,8 @@ android_test { ], libs: [ - "android.test.base", - "android.test.runner", + "android.test.base.stubs.system", + "android.test.runner.stubs.system", ], static_libs: [ diff --git a/PermissionController/tests/permissionui/Android.bp b/PermissionController/tests/permissionui/Android.bp index 6aa0dc45c..6e1839d1d 100644 --- a/PermissionController/tests/permissionui/Android.bp +++ b/PermissionController/tests/permissionui/Android.bp @@ -38,17 +38,20 @@ android_test { "src/**/*.kt", ], libs: [ - "android.test.base", - "android.test.runner", + "android.test.base.stubs.test", + "android.test.runner.stubs.test", ], static_libs: [ - "androidx.test.rules", - "androidx.test.ext.truth", "androidx.test.ext.junit", + "androidx.test.ext.truth", + "androidx.test.rules", "androidx.test.uiautomator_uiautomator", + "com.android.permission.flags-aconfig-java", "compatibility-device-util-axt", + "flag-junit", "permission-test-util-lib", + "platform-test-annotations", ], test_suites: [ @@ -58,6 +61,7 @@ android_test { ], data: [ + ":CtsAppThatRequestsLocationAndBackgroundPermission31", ":CtsAppThatRequestsLocationPermission29", ":PermissionUiUseStoragePermissionApp", ":PermissionUiUseCameraPermissionApp", diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml index 566410777..a4aa03abe 100644 --- a/PermissionController/tests/permissionui/AndroidTest.xml +++ b/PermissionController/tests/permissionui/AndroidTest.xml @@ -37,6 +37,8 @@ <!-- Load additional APKs onto device --> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="CtsAppThatRequestsLocationAndBackgroundPermission31.apk" + value="/data/local/tmp/pc-permissionui/AppThatRequestsLocationAndBackground.apk" /> <option name="push-file" key="CtsAppThatRequestsLocationPermission29.apk" value="/data/local/tmp/pc-permissionui/AppThatRequestsLocation.apk" /> <option name="push-file" key="PermissionUiUseStoragePermissionApp.apk" diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AutoAppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AutoAppPermissionFragmentTest.kt new file mode 100644 index 000000000..a5420e067 --- /dev/null +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AutoAppPermissionFragmentTest.kt @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permissionui.ui + +import android.Manifest.permission.ACCESS_COARSE_LOCATION +import android.Manifest.permission.ACCESS_FINE_LOCATION +import android.content.Intent +import android.os.UserHandle +import android.os.UserHandle.myUserId +import android.permission.cts.PermissionUtils +import android.permission.cts.PermissionUtils.grantPermission +import android.permission.cts.PermissionUtils.revokePermission +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.uiautomator.By +import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity +import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject +import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull +import com.android.compatibility.common.util.UiAutomatorUtils2.waitUntilObjectGone +import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags +import com.android.permissioncontroller.permissionui.wakeUpScreen +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertNotNull +import junit.framework.Assert.assertNull +import junit.framework.Assert.assertTrue +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 + +private const val LOCATION = "Location" +private const val PERMISSION_REQUEST_APP = "PermissionRequestApp" +private const val SWITCH_VIEW = "Use precise location" +private const val LOG_TAG = "AppPermissionFragmentTest" +private const val DONT_ALLOW = "Don\u2019t allow" +private const val ALLOW_ONLY = "Allow only while using the app" + +/** + * Simple tests for {@link AutoAppPermissionFragment} + * + * Run with: atest AutoAppPermissionFragmentTest + */ +@RunWith(AndroidJUnit4::class) +class AutoAppPermissionFragmentTest : BasePermissionUiTest() { + + private val USER_PKG_V2 = "android.permission.cts.appthatrequestpermission" + + @JvmField @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Before fun assumeIsAuto() = assumeTrue(isAutomotive) + + @Before + fun setup() { + wakeUpScreen() + assumeTrue("Location Accuracy is only available on S+", SdkLevel.isAtLeastS()) + installTestAppThatRequestsFineLocation() + grantPermission(USER_PKG_V2, ACCESS_FINE_LOCATION) + grantPermission(USER_PKG_V2, ACCESS_COARSE_LOCATION) + } + + private fun startManagePermissionAppsActivity() { + runWithShellPermissionIdentity { + instrumentationContext.startActivity( + Intent(Intent.ACTION_MANAGE_APP_PERMISSION).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + putExtra(Intent.EXTRA_PACKAGE_NAME, USER_PKG_V2) + putExtra(Intent.EXTRA_PERMISSION_NAME, ACCESS_FINE_LOCATION) + putExtra(Intent.EXTRA_USER, UserHandle.of(myUserId())) + } + ) + } + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenSwitchViewOffCoarseLocationGranted() { + // Pre: Assert switch view exists and fine location granted + // Test: Find and toggle switch view + // Post-Condition: Assert that coarse location granted + + startManagePermissionAppsActivity() + + // confirm switch view exists and is currently on + assertNotNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + assertTrue(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_FINE_LOCATION)) + + // turn off switch view and confirm functionality + waitFindObject(By.text(SWITCH_VIEW)).click() + assertFalse(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_FINE_LOCATION)) + assertTrue(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_COARSE_LOCATION)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenSwitchViewOnFineLocationGranted() { + // Pre: Assert switch view exists and fine location not granted + // Test: Find and toggle switch view + // Post-Condition: Assert that fine location granted + + revokePermission(USER_PKG_V2, ACCESS_FINE_LOCATION) + startManagePermissionAppsActivity() + + // confirm switch view exists and is currently off + assertNotNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + assertFalse(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_FINE_LOCATION)) + + // turn on switch view and confirm functionality + waitFindObject(By.text(SWITCH_VIEW)).click() + assertTrue(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_FINE_LOCATION)) + assertTrue(PermissionUtils.isPermissionGranted(USER_PKG_V2, ACCESS_COARSE_LOCATION)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenLocationPermissionsDeniedSwitchViewGone() { + // Pre: Assert switch view visible + // Test: Click "Don't Allow" + // Post-Condition: Assert switch view is not visible + + startManagePermissionAppsActivity() + + // confirm switch view exists + assertNotNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + + // turn off location permissions and confirm functionality + waitFindObject(By.text(DONT_ALLOW)).click() + assertNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenLocationPermissionsGrantedSwitchViewVisible() { + // Test: Click "Allow always" + // Post-Condition: Assert switch view is visible + + revokePermission(USER_PKG_V2, ACCESS_FINE_LOCATION) + revokePermission(USER_PKG_V2, ACCESS_COARSE_LOCATION) + startManagePermissionAppsActivity() + + // confirm switch view doesn't exist + assertNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + + // allow location permissions and confirm functionality + waitFindObject(By.text(ALLOW_ONLY)).click() + assertNotNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenFlaggedTurnedOnSwitchViewVisible() { + startManagePermissionAppsActivity() + assertNotNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_COARSE_FINE_LOCATION_PROMPT_FOR_AAOS) + fun whenFlaggedTurnedOffSwitchViewNotVisible() { + startManagePermissionAppsActivity() + waitUntilObjectGone(By.text(SWITCH_VIEW)) + assertNull(waitFindObjectOrNull(By.text(SWITCH_VIEW))) + } + + @After + fun uninstallTestApp() { + uninstallTestApps() + } +} diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt index 2cb7e7a5c..995e22945 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/BasePermissionUiTest.kt @@ -34,6 +34,7 @@ abstract class BasePermissionUiTest { hasSystemFeature(PackageManager.FEATURE_LEANBACK) || hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY) } + protected val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) @Rule fun disableAnimationsRule() = DisableAnimationRule() } diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt index 0f4f3841a..ecc7e161f 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt @@ -90,26 +90,6 @@ class HealthConnectAllAppPermissionFragmentTest : BasePermissionUiTest() { } } - @Test - fun invalidGrantedUsedHealthConnectPermissionsAreListed() { - installInvalidTestAppThatUsesHealthConnectPermission() - grantTestAppPermission(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED) - - startManageAppPermissionsActivity() - - // Ensure that Health Connect permission group permissions are present if a single one is - // already granted, regardless of whether the intent filters are incorrectly or not setup - // for the app - eventually { - waitFindObject(By.text(HEALTH_CONNECT_LABEL)) - waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED_LABEL)) - - // READ_STEPS is not granted, but should still be present due to READ_FLOORS_CLIMBED - // being granted - waitFindObject(By.text(HEALTH_CONNECT_PERMISSION_READ_STEPS_LABEL)) - } - } - private fun startManageAppPermissionsActivity() { uiDevice.performActionAndWait( { diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt index 04dbac39f..b2d47a7d7 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt @@ -70,16 +70,6 @@ class HealthConnectAppPermissionFragmentTest : BasePermissionUiTest() { waitUntilObjectGone(By.text(HEALTH_CONNECT_LABEL), TIMEOUT_SHORT) } - @Test - fun invalidGrantedUsedHealthConnectPermissionsAreListed() { - installInvalidTestAppThatUsesHealthConnectPermission() - grantTestAppPermission(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED) - - startManageAppPermissionsActivity() - - eventually { waitFindObject(By.text(HEALTH_CONNECT_LABEL)) } - } - private fun startManageAppPermissionsActivity() { runWithShellPermissionIdentity { instrumentationContext.startActivity( diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt index 09f59de30..7227ea3d8 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt @@ -23,6 +23,8 @@ import android.permission.cts.PermissionUtils.uninstallApp // Test Apps' APK files private const val APK_DIRECTORY = "/data/local/tmp/pc-permissionui/" private const val LOCATION_PERM_USER_APK = "$APK_DIRECTORY/AppThatRequestsLocation.apk" +private const val FINE_AND_BACKGROUND_LOCATION_PERM_USER_APK = + "$APK_DIRECTORY/AppThatRequestsLocationAndBackground.apk" private const val CAMERA_PERM_USER_APK = "$APK_DIRECTORY/PermissionUiUseCameraPermissionApp.apk" private const val HEALTH_CONNECT_PERMISSION_USER_APK = "$APK_DIRECTORY/PermissionUiUseHealthConnectPermissionApp.apk" @@ -52,6 +54,8 @@ const val TEST_APP_DEFINED_PERMISSION_C_LABEL = "Permission C" fun installTestAppThatRequestsLocation() = install(LOCATION_PERM_USER_APK) +fun installTestAppThatRequestsFineLocation() = install(FINE_AND_BACKGROUND_LOCATION_PERM_USER_APK) + fun installTestAppThatUsesCameraPermission() = install(CAMERA_PERM_USER_APK) fun installTestAppThatUsesHealthConnectPermission() = install(HEALTH_CONNECT_PERMISSION_USER_APK) diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt index eb7be564b..b38f5f40a 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageCustomPermissionsFragmentTest.kt @@ -26,10 +26,12 @@ import androidx.test.uiautomator.By import com.android.compatibility.common.util.SystemUtil.eventually import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject +import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull import com.android.permissioncontroller.permissionui.getUsageCountsFromUi import com.android.permissioncontroller.permissionui.wakeUpScreen import com.google.common.truth.Truth.assertThat import org.junit.After +import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -47,6 +49,7 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() { private val PERM_LABEL = "Permission A" private val PERM = "com.android.permissioncontroller.tests.A" private val ADDITIONAL_PERMISSIONS_LABEL = "Additional permissions" + private val BODY_SENSORS_LABEL = "Body sensors" @Before fun setup() { @@ -92,6 +95,14 @@ class ManageCustomPermissionsFragmentTest : BaseHandheldPermissionUiTest() { eventually { assertThat(getUsageCountsFromUi(PERM_LABEL)).isEqualTo(original) } } + @Test + fun bodySensorsEitherDisplayedInMainPageOrInAdditional() { + if (waitFindObjectOrNull(By.textContains(BODY_SENSORS_LABEL)) == null) { + waitFindObject(By.textContains(ADDITIONAL_PERMISSIONS_LABEL)).click() + assertNotNull(waitFindObjectOrNull(By.textContains(BODY_SENSORS_LABEL))) + } + } + @After fun tearDown() { uninstallApp(DEFINER_PKG) diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt index 1ad876245..fcce09450 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt @@ -23,6 +23,8 @@ import android.permission.cts.PermissionUtils.grantPermission import android.permission.cts.PermissionUtils.install import android.permission.cts.PermissionUtils.revokePermission import android.permission.cts.PermissionUtils.uninstallApp +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.uiautomator.By @@ -30,17 +32,23 @@ import com.android.compatibility.common.util.SystemUtil.eventually import com.android.compatibility.common.util.SystemUtil.getEventually import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull +import com.android.permission.flags.Flags import com.android.permissioncontroller.permissionui.getUsageCountsFromUi import com.android.permissioncontroller.permissionui.wakeUpScreen import com.google.common.truth.Truth.assertThat import org.junit.After +import org.junit.Assert.assertNull import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith /** Simple tests for {@link ManageStandardPermissionsFragment} */ @RunWith(AndroidJUnit4::class) class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { + + @JvmField @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before fun setup() { wakeUpScreen() @@ -117,7 +125,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { assertThat(afterInstall.granted).isEqualTo(original.granted) assertThat(afterInstall.total).isEqualTo(original.total + 1) }, - TIMEOUT + TIMEOUT, ) } @@ -127,13 +135,13 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { install(LOCATION_USER_APK) eventually( { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isNotEqualTo(original) }, - TIMEOUT + TIMEOUT, ) uninstallApp(LOCATION_USER_PKG) eventually( { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isEqualTo(original) }, - TIMEOUT + TIMEOUT, ) } @@ -147,7 +155,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total) .isEqualTo(original.total + 1) }, - TIMEOUT + TIMEOUT, ) grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION) @@ -170,7 +178,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted) .isNotEqualTo(original.granted) }, - TIMEOUT + TIMEOUT, ) revokePermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION) @@ -190,7 +198,7 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore + 1) }, - TIMEOUT + TIMEOUT, ) } @@ -202,13 +210,13 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { install(ADDITIONAL_USER_APK) eventually( { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) }, - TIMEOUT + TIMEOUT, ) uninstallApp(ADDITIONAL_USER_PKG) eventually( { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) }, - TIMEOUT + TIMEOUT, ) } @@ -220,16 +228,23 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() { install(ADDITIONAL_USER_APK) eventually( { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) }, - TIMEOUT + TIMEOUT, ) uninstallApp(ADDITIONAL_DEFINER_PKG) eventually( { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) }, - TIMEOUT + TIMEOUT, ) } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DECLUTTERED_PERMISSION_MANAGER_ENABLED) + fun noUnusedPermissionGroupDisplayedInTheMainPage() { + eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0 of 0")))) } + eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0/0")))) } + } + companion object { private val LOG_TAG = ManageStandardPermissionsFragmentTest::class.java.simpleName diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt index f81b620ce..ea3868c9d 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/v31/PermissionUsageFragmentTest.kt @@ -32,6 +32,7 @@ import com.android.permissioncontroller.permissionui.PermissionHub2Test import com.android.permissioncontroller.permissionui.pressHome import com.android.permissioncontroller.permissionui.wakeUpScreen import org.junit.After +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -45,6 +46,24 @@ class PermissionUsageFragmentTest : PermissionHub2Test() { private val APP_LABEL = "CameraRequestApp" private val CAMERA_PREF_LABEL = "Camera" + /** + * Returns `true` if [Intent.ACTION_REVIEW_PERMISSION_USAGE] is handled by permission controller + */ + private fun isPrivacyDashboardProvidedByPermissionController(): Boolean { + val pm = context.packageManager + return pm.resolveActivity(Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE), 0)!! + .activityInfo + .packageName == pm.permissionControllerPackageName + } + + @Before + fun checkPreconditions() { + // Since there is no clear UI specification of privacy dashboard, OEM could have their + // own implementation with a different UI. Hence, limit the test scope to only permission + // controller. + assumeTrue(isPrivacyDashboardProvidedByPermissionController()) + } + @Before fun setup() { wakeUpScreen() diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt index 2d338906b..9f0f548e0 100644 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt +++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/ManagePermissionsOtherFragmentTest.kt @@ -22,6 +22,7 @@ import com.android.permissioncontroller.permissionui.ui.TEST_APP_DEFINED_PERMISS import com.android.permissioncontroller.permissionui.ui.grantTestAppPermission import com.android.permissioncontroller.permissionui.ui.installTestAppThatDefinesAdditionalPermissions import com.android.permissioncontroller.permissionui.ui.installTestAppThatUsesAdditionalPermission +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -49,6 +50,11 @@ class ManagePermissionsOtherFragmentTest : TelevisionUiBaseTest() { ) } + @After + fun goOutOfOtherPermissionsScreen() { + uiDevice.pressBack() + } + @Test fun bodySensors_permissionGroup_isNotShown() { assertFalse( diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS deleted file mode 100644 index c6774752d..000000000 --- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -# Primary maintainer - -# Other maintainers (ATV Core Framework team) -bronger@google.com -galinap@google.com -philipjunker@google.com -rgl@google.com -valiiftime@google.com diff --git a/SafetyCenter/Config/Android.bp b/SafetyCenter/Config/Android.bp index a8d275226..32df3165e 100644 --- a/SafetyCenter/Config/Android.bp +++ b/SafetyCenter/Config/Android.bp @@ -35,7 +35,7 @@ java_library { libs: [ "androidx.annotation_annotation", "framework-annotations-lib", - "framework-permission-s", + "framework-permission-s.impl", "safety-center-annotations", ], static_libs: [ diff --git a/SafetyCenter/OWNERS b/SafetyCenter/OWNERS index d9039e915..229331d3c 100644 --- a/SafetyCenter/OWNERS +++ b/SafetyCenter/OWNERS @@ -4,7 +4,6 @@ simonjw@google.com davidcoffin@google.com elliotsisteron@google.com fiscella@google.com -shrigupt@google.com jtomljanovic@google.com deweytyl@google.com maxspencer@google.com diff --git a/SafetyCenter/Resources/Android.bp b/SafetyCenter/Resources/Android.bp index a10ea7f1a..f485172f4 100644 --- a/SafetyCenter/Resources/Android.bp +++ b/SafetyCenter/Resources/Android.bp @@ -49,6 +49,7 @@ android_app { static_libs: [ "SafetyCenterResourcesShared", ], + updatable: true, } android_app_certificate { diff --git a/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml new file mode 100644 index 000000000..de033ac44 --- /dev/null +++ b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml @@ -0,0 +1,158 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<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" + notificationsAllowed="true"/> + <dynamic-safety-source + id="AndroidBiometrics" + packageName="com.android.settings" + profile="all_profiles" + title="@com.android.safetycenter.resources:string/biometrics_title" + titleForWork="@com.android.safetycenter.resources:string/biometrics_title_for_work" + titleForPrivateProfile="@com.android.safetycenter.resources:string/biometrics_title_for_private_profile" + searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms" + initialDisplayState="hidden"/> + </safety-sources-group> + <safety-sources-group + id="AndroidCellularNetworkSecuritySources" + title="@com.android.safetycenter.resources:string/cellular_network_security_title" + summary="@com.android.safetycenter.resources:string/cellular_network_security_summary"> + <dynamic-safety-source + id="AndroidCellularNetworkSecurity" + packageName="com.android.phone" + profile="primary_profile_only" + notificationsAllowed="true" + 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="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"/> + <dynamic-safety-source + id="AndroidHealthConnect" + profile="primary_profile_only" + packageName="com.android.healthconnect.controller" + initialDisplayState="hidden" + refreshOnPageOpenAllowed="false" + title="@com.android.safetycenter.resources:string/health_connect_title" + searchTerms="@com.android.safetycenter.resources:string/health_connect_search_terms"/> + <dynamic-safety-source + id="AndroidPrivacyAppDataSharingUpdates" + packageName="com.android.permissioncontroller" + profile="primary_profile_only" + initialDisplayState="hidden" + refreshOnPageOpenAllowed="true" + title="@com.android.safetycenter.resources:string/app_data_sharing_updates_title" + searchTerms="@com.android.safetycenter.resources:string/app_data_sharing_updates_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" + notificationsAllowed="true" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidNotificationListener" + packageName="com.android.permissioncontroller" + profile="primary_profile_only" + notificationsAllowed="true" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidBackgroundLocation" + packageName="com.android.permissioncontroller" + profile="all_profiles" + notificationsAllowed="true" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidPermissionAutoRevoke" + packageName="com.android.permissioncontroller" + profile="all_profiles" + notificationsAllowed="true" + refreshOnPageOpenAllowed="true"/> + <issue-only-safety-source + id="AndroidCertificateTransparency" + packageName="android" + profile="primary_profile_only" + notificationsAllowed="false" + refreshOnPageOpenAllowed="true"/> + </safety-sources-group> + <safety-sources-group + id="AndroidPrivacySourcesAdditional" + title="@com.android.safetycenter.resources:string/privacy_additional_title"> + <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"/> + <dynamic-safety-source + id="AndroidPrivateSpace" + packageName="com.android.settings" + profile="primary_profile_only" + title="@com.android.safetycenter.resources:string/private_space_title" + summary="@com.android.safetycenter.resources:string/private_space_summary" + searchTerms="@com.android.safetycenter.resources:string/private_space_search_terms" + initialDisplayState="hidden" + maxSeverityLevel="0"/> + </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" + title="@com.android.safetycenter.resources:string/work_policy_title" + initialDisplayState="hidden" + refreshOnPageOpenAllowed="true"/> + <static-safety-source + id="AndroidMoreSettings" + profile="primary_profile_only" + intentAction="com.android.settings.MORE_SECURITY_PRIVACY_SETTINGS" + title="@com.android.safetycenter.resources:string/more_settings_title" + summary="@com.android.safetycenter.resources:string/more_settings_summary" + searchTerms="@com.android.safetycenter.resources:string/more_settings_search_terms"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Resources/res/values-ar/strings.xml b/SafetyCenter/Resources/res/values-ar/strings.xml index 2a97af99a..0fc5079db 100644 --- a/SafetyCenter/Resources/res/values-ar/strings.xml +++ b/SafetyCenter/Resources/res/values-ar/strings.xml @@ -24,7 +24,7 @@ <string name="lock_screen_summary_disabled" msgid="354071230916616692">"ما مِن معلومات بعد."</string> <string name="lock_screen_search_terms" msgid="2678486357779794826">"قفل الجهاز، قفل الشاشة، شاشة القفل، شاشة قفل، كلمة المرور، رقم التعريف الشخصي، نقش"</string> <string name="biometrics_title" msgid="5859504610285212938">"المقاييس الحيوية"</string> - <string name="biometrics_search_terms" msgid="6040319118762671981">"بصمة الإصبع، إصبع، إضافة بصمة إصبع، فتح الجهاز بالتعرف على الوجه، وجه"</string> + <string name="biometrics_search_terms" msgid="6040319118762671981">"بصمة الإصبع، إصبع، إضافة بصمة إصبع، فتح الجهاز ببصمة الوجه، وجه"</string> <string name="privacy_sources_title" msgid="4061110826457365957">"الخصوصية"</string> <string name="privacy_sources_summary" msgid="4089719981155120864">"لوحة البيانات والأذونات وعناصر التحكّم"</string> <string name="permission_usage_title" msgid="3633779688945350407">"لوحة بيانات الخصوصية"</string> @@ -36,7 +36,7 @@ <string name="privacy_controls_title" msgid="5322875777945432395">"عناصر التحكّم في الخصوصية"</string> <string name="privacy_controls_summary" msgid="2402066941190435424">"التحكُّم في وصول الجهاز إلى الميكروفون والكاميرا وغير ذلك"</string> <string name="privacy_controls_search_terms" msgid="3774472175934304165">"الخصوصية، عناصر التحكّم في الخصوصية"</string> - <string name="advanced_title" msgid="8745436380690561172">"الإعدادات المتقدّمة"</string> + <string name="advanced_title" msgid="8745436380690561172">"إعدادات إضافية"</string> <string name="advanced_security_title" msgid="1126833338772188155">"المزيد من إعدادات الأمان"</string> <string name="advanced_security_summary" msgid="6172253327022425123">"التشفير وبيانات الاعتماد وغير ذلك"</string> <string name="advanced_security_search_terms" msgid="3350609555814362075"></string> diff --git a/SafetyCenter/Resources/res/values-bn-v35/strings.xml b/SafetyCenter/Resources/res/values-bn-v35/strings.xml index 644f8fa18..0837061ed 100644 --- a/SafetyCenter/Resources/res/values-bn-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-bn-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"গোপনীয়তা নিয়ন্ত্রণ"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"অনুমতি, কন্ট্রোল"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"ব্যক্তিগত স্পেস"</string> + <string name="private_space_title" msgid="6158245041481535879">"প্রাইভেট স্পেস"</string> <string name="private_space_summary" msgid="529869826714610294">"\'ব্যক্তিগত স্পেস\' সেট-আপ ও আরও অনেক কিছু করুন"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"ব্যক্তিগত স্পেস"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"প্রাইভেট স্পেস"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-de-v34/strings.xml b/SafetyCenter/Resources/res/values-de-v34/strings.xml index 4289c14a0..8f75bdfbb 100644 --- a/SafetyCenter/Resources/res/values-de-v34/strings.xml +++ b/SafetyCenter/Resources/res/values-de-v34/strings.xml @@ -25,7 +25,7 @@ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Änderungen bei der Weitergabe von Standortdaten"</string> <string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Daten, Datenweitergabe, Aktualisierungen der Datenweitergabe, Aktualisierungen der Weitergabe von Standortdaten, Weitergabe"</string> <string name="advanced_title" msgid="6259362998269627310">"Weitere Einstellungen"</string> - <string name="more_settings_title" msgid="9033454654010697185">"Mehr Sicherheit und Datenschutz"</string> + <string name="more_settings_title" msgid="9033454654010697185">"Mehr Sicherheit & Datenschutz"</string> <string name="more_settings_summary" msgid="7086620830002515710">"Autofill, Benachrichtigungen und mehr"</string> <string name="more_settings_search_terms" msgid="1371913937610933955"></string> <string name="work_policy_title" msgid="915692932391542104">"Informationen zu den Arbeitsrichtlinien"</string> diff --git a/SafetyCenter/Resources/res/values-fa-v35/strings.xml b/SafetyCenter/Resources/res/values-fa-v35/strings.xml index 878c10bb4..40072433d 100644 --- a/SafetyCenter/Resources/res/values-fa-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-fa-v35/strings.xml @@ -21,7 +21,7 @@ <string name="cellular_network_security_summary" msgid="7319307247487475572">"نوع شبکه، رمزگذاری، کنترلهای اعلان"</string> <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string> <string name="privacy_title" msgid="7047524783080782769">"حریم خصوصی"</string> - <string name="privacy_sources_title" msgid="309304028326660956">"تنظیمات حریم خصوصی"</string> + <string name="privacy_sources_title" msgid="309304028326660956">"کنترلهای حریم خصوصی"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"اجازهها، کنترلها"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> <string name="private_space_title" msgid="6158245041481535879">"فضای خصوصی"</string> diff --git a/SafetyCenter/Resources/res/values-fa/strings.xml b/SafetyCenter/Resources/res/values-fa/strings.xml index bdf2063f1..01835a8e5 100644 --- a/SafetyCenter/Resources/res/values-fa/strings.xml +++ b/SafetyCenter/Resources/res/values-fa/strings.xml @@ -33,9 +33,9 @@ <string name="permission_manager_title" msgid="5277347862821255015">"مدیر اجازهها"</string> <string name="permission_manager_summary" msgid="8099852107340970790">"کنترل دسترسی برنامه به دادههای شما"</string> <string name="permission_manager_search_terms" msgid="2895147613099694722">"اجازهها، مدیر اجازهها"</string> - <string name="privacy_controls_title" msgid="5322875777945432395">"تنظیمات حریم خصوصی"</string> + <string name="privacy_controls_title" msgid="5322875777945432395">"کنترلهای حریم خصوصی"</string> <string name="privacy_controls_summary" msgid="2402066941190435424">"کنترل دسترسی دستگاه به میکروفون، دوربین، و غیره"</string> - <string name="privacy_controls_search_terms" msgid="3774472175934304165">"حریم خصوصی، تنظیمات حریم خصوصی"</string> + <string name="privacy_controls_search_terms" msgid="3774472175934304165">"حریم خصوصی، کنترلهای حریم خصوصی"</string> <string name="advanced_title" msgid="8745436380690561172">"تنظیمات بیشتر"</string> <string name="advanced_security_title" msgid="1126833338772188155">"تنظیمات ایمنی بیشتر"</string> <string name="advanced_security_summary" msgid="6172253327022425123">"رمزگذاری، اطلاعات اعتباری، و غیره"</string> diff --git a/SafetyCenter/Resources/res/values-fi-v34/strings.xml b/SafetyCenter/Resources/res/values-fi-v34/strings.xml index 20d88f01a..b51af6e54 100644 --- a/SafetyCenter/Resources/res/values-fi-v34/strings.xml +++ b/SafetyCenter/Resources/res/values-fi-v34/strings.xml @@ -25,7 +25,7 @@ <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Sijaintidatan jakamisen päivitykset"</string> <string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Data, datan jakaminen, datan jakamisen päivitykset, sijaintidatan jakamisen päivitykset, jakaminen"</string> <string name="advanced_title" msgid="6259362998269627310">"Muut asetukset"</string> - <string name="more_settings_title" msgid="9033454654010697185">"Lisää suojaus‑ ja yksityisyysasetuksia"</string> + <string name="more_settings_title" msgid="9033454654010697185">"Lisää turvallisuus‑ ja yksityisyysasetuksia"</string> <string name="more_settings_summary" msgid="7086620830002515710">"Automaattinen täyttö, ilmoitukset ja muuta"</string> <string name="more_settings_search_terms" msgid="1371913937610933955"></string> <string name="work_policy_title" msgid="915692932391542104">"Työkäytäntötietosi"</string> diff --git a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml index a835c7139..a96007f58 100644 --- a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml +++ b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml @@ -28,10 +28,10 @@ <string name="privacy_sources_title" msgid="4061110826457365957">"Confidentialité"</string> <string name="privacy_sources_summary" msgid="4089719981155120864">"Tableau de bord, autorisations, commandes"</string> <string name="permission_usage_title" msgid="3633779688945350407">"Tableau de bord de confidentialité"</string> - <string name="permission_usage_summary" msgid="5323079206029964468">"Affichez les applications qui ont récemment utilisé les autorisations"</string> + <string name="permission_usage_summary" msgid="5323079206029964468">"Affichez les applis qui ont récemment utilisé les autorisations"</string> <string name="permission_usage_search_terms" msgid="3852343592870257104">"Confidentialité, Tableau de bord de confidentialité"</string> <string name="permission_manager_title" msgid="5277347862821255015">"Gestionnaire des autorisations"</string> - <string name="permission_manager_summary" msgid="8099852107340970790">"Gérez l\'accès des applications à vos données"</string> + <string name="permission_manager_summary" msgid="8099852107340970790">"Gérez l\'accès des applis à vos données"</string> <string name="permission_manager_search_terms" msgid="2895147613099694722">"Autorisations, Gestionnaire des autorisations"</string> <string name="privacy_controls_title" msgid="5322875777945432395">"Paramètres de confidentialité"</string> <string name="privacy_controls_summary" msgid="2402066941190435424">"Contrôlez l\'accès de l\'appareil au microphone, à l\'appareil photo, etc."</string> diff --git a/SafetyCenter/Resources/res/values-hy-v35/strings.xml b/SafetyCenter/Resources/res/values-hy-v35/strings.xml index dd5c9e01b..d5d9f39c4 100644 --- a/SafetyCenter/Resources/res/values-hy-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-hy-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"Գաղտնիության կարգավորումներ"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"Թույլտվություններ, կարգավորումներ"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"Անձնական տարածք"</string> + <string name="private_space_title" msgid="6158245041481535879">"Մասնավոր տարածք"</string> <string name="private_space_summary" msgid="529869826714610294">"Կարգավորեք մասնավոր տարածքը և ավելին"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"Անձնական տարածք"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"Մասնավոր տարածք"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-ja/strings.xml b/SafetyCenter/Resources/res/values-ja/strings.xml index b31b1690a..6e67998f4 100644 --- a/SafetyCenter/Resources/res/values-ja/strings.xml +++ b/SafetyCenter/Resources/res/values-ja/strings.xml @@ -30,9 +30,9 @@ <string name="permission_usage_title" msgid="3633779688945350407">"プライバシー ダッシュボード"</string> <string name="permission_usage_summary" msgid="5323079206029964468">"権限を最近使用したアプリが表示されます"</string> <string name="permission_usage_search_terms" msgid="3852343592870257104">"プライバシー, プライバシー ダッシュボード"</string> - <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャ"</string> + <string name="permission_manager_title" msgid="5277347862821255015">"権限マネージャー"</string> <string name="permission_manager_summary" msgid="8099852107340970790">"アプリのデータアクセスを管理します"</string> - <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャ"</string> + <string name="permission_manager_search_terms" msgid="2895147613099694722">"権限, 権限マネージャー"</string> <string name="privacy_controls_title" msgid="5322875777945432395">"プライバシー管理"</string> <string name="privacy_controls_summary" msgid="2402066941190435424">"マイク、カメラなどへのデバイス アクセスを管理します"</string> <string name="privacy_controls_search_terms" msgid="3774472175934304165">"プライバシー, プライバシー管理"</string> diff --git a/SafetyCenter/Resources/res/values-ka-v35/strings.xml b/SafetyCenter/Resources/res/values-ka-v35/strings.xml index d5a444deb..6d434bca0 100644 --- a/SafetyCenter/Resources/res/values-ka-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-ka-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"კონფიდენციალურობის მართვის პარამეტრები"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"ნებართვები, მართვის საშუალებები"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"პირადი სივრცე"</string> + <string name="private_space_title" msgid="6158245041481535879">"კერძო სივრცე"</string> <string name="private_space_summary" msgid="529869826714610294">"დააყენეთ პირადი სივრცე და ა.შ."</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"პირადი სივრცე"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"კერძო სივრცე"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-kn-v35/strings.xml b/SafetyCenter/Resources/res/values-kn-v35/strings.xml index 339e522d9..c054b2fa1 100644 --- a/SafetyCenter/Resources/res/values-kn-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-kn-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"ಗೌಪ್ಯತೆ ನಿಯಂತ್ರಣಗಳು"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"ಅನುಮತಿಗಳು, ನಿಯಂತ್ರಣಗಳು"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string> + <string name="private_space_title" msgid="6158245041481535879">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="private_space_summary" msgid="529869826714610294">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ ಹಾಗೂ ಇನ್ನಷ್ಟನ್ನು ಮಾಡಿ"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-lo-v35/strings.xml b/SafetyCenter/Resources/res/values-lo-v35/strings.xml index 606339144..4d04dc07a 100644 --- a/SafetyCenter/Resources/res/values-lo-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-lo-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"ການຄວບຄຸມຄວາມເປັນສ່ວນຕົວ"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"ການອະນຸຍາດ, ການຄວບຄຸມ"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"ພື້ນທີ່ສ່ວນຕົວ"</string> + <string name="private_space_title" msgid="6158245041481535879">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="private_space_summary" msgid="529869826714610294">"ຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ ແລະ ອື່ນໆ"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"ພື້ນທີ່ສ່ວນຕົວ"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-lv-v35/strings.xml b/SafetyCenter/Resources/res/values-lv-v35/strings.xml index f26fcccc3..e3cf556bf 100644 --- a/SafetyCenter/Resources/res/values-lv-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-lv-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"Konfidencialitātes vadīklas"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"Atļaujas, vadīklas"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"Privātā mape"</string> + <string name="private_space_title" msgid="6158245041481535879">"Privātā telpa"</string> <string name="private_space_summary" msgid="529869826714610294">"Privātās mapes iestatīšana un citas iespējas"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"Privātā mape"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"Privātā telpa"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-or-v35/strings.xml b/SafetyCenter/Resources/res/values-or-v35/strings.xml index 5023efd4d..4a0ba87a5 100644 --- a/SafetyCenter/Resources/res/values-or-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-or-v35/strings.xml @@ -21,7 +21,7 @@ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ନେଟୱାର୍କ ପ୍ରକାର, ଏନକ୍ରିପସନ, ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string> <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string> <string name="privacy_title" msgid="7047524783080782769">"ଗୋପନୀୟତା"</string> - <string name="privacy_sources_title" msgid="309304028326660956">"ଗୋପନୀୟତା ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string> + <string name="privacy_sources_title" msgid="309304028326660956">"ଗୋପନୀୟତା ନିୟନ୍ତ୍ରଣ"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"ଅନୁମତି, ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> <string name="private_space_title" msgid="6158245041481535879">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> diff --git a/SafetyCenter/Resources/res/values-pa-v35/strings.xml b/SafetyCenter/Resources/res/values-pa-v35/strings.xml index 9ea3ee068..2196a55d1 100644 --- a/SafetyCenter/Resources/res/values-pa-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-pa-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"ਪਰਦੇਦਾਰੀ ਕੰਟਰੋਲ"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"ਇਜਾਜ਼ਤਾਂ, ਕੰਟਰੋਲ"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"ਨਿੱਜੀ ਸਪੇਸ"</string> - <string name="private_space_summary" msgid="529869826714610294">"ਨਿੱਜੀ ਸਪੇਸ ਦਾ ਸੈੱਟਅੱਪ ਅਤੇ ਹੋਰ ਕੰਮ ਕਰੋ"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"ਨਿੱਜੀ ਸਪੇਸ"</string> + <string name="private_space_title" msgid="6158245041481535879">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> + <string name="private_space_summary" msgid="529869826714610294">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਦਾ ਸੈੱਟਅੱਪ ਅਤੇ ਹੋਰ ਕੰਮ ਕਰੋ"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-ru-v34/strings.xml b/SafetyCenter/Resources/res/values-ru-v34/strings.xml index 70b139c91..397ef7b9d 100644 --- a/SafetyCenter/Resources/res/values-ru-v34/strings.xml +++ b/SafetyCenter/Resources/res/values-ru-v34/strings.xml @@ -22,7 +22,7 @@ <string name="privacy_sources_summary" msgid="4083646673569677049">"Разрешения, панель управления, параметры"</string> <string name="health_connect_title" msgid="8318152190040327804">"Здоровье и спорт"</string> <string name="health_connect_search_terms" msgid="4998970586245680829">"Здоровье, Здоровье и спорт"</string> - <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Обновления в передаче данных о местоположении"</string> + <string name="app_data_sharing_updates_title" msgid="7428862330643262588">"Изменения в передаче геоданных"</string> <string name="app_data_sharing_updates_search_terms" msgid="8414777373734245398">"Данные, Передача данных, Обновления передачи данных, Обновление доступа к данным, Обновление доступа к данным о местоположении"</string> <string name="advanced_title" msgid="6259362998269627310">"Другие настройки"</string> <string name="more_settings_title" msgid="9033454654010697185">"Дополнительные настройки защиты и конфиденциальности"</string> diff --git a/SafetyCenter/Resources/res/values-si-v35/strings.xml b/SafetyCenter/Resources/res/values-si-v35/strings.xml index bcff52b2a..41025a3a4 100644 --- a/SafetyCenter/Resources/res/values-si-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-si-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"පෞද්ගලිකත්ව පාලන"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"අවසර, පාලන"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"පෞද්ගලික ඉඩ"</string> + <string name="private_space_title" msgid="6158245041481535879">"රහසිගත අවකාශය"</string> <string name="private_space_summary" msgid="529869826714610294">"පෞද්ගලික ඉඩ, සහ තවත් දේ පිහිටුවන්න"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"පෞද්ගලික ඉඩ"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"රහසිගත අවකාශය"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-sv-v35/strings.xml b/SafetyCenter/Resources/res/values-sv-v35/strings.xml index 77a6de01f..974916040 100644 --- a/SafetyCenter/Resources/res/values-sv-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-sv-v35/strings.xml @@ -22,9 +22,9 @@ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string> <string name="privacy_title" msgid="7047524783080782769">"Integritet"</string> <string name="privacy_sources_title" msgid="309304028326660956">"Integritetsinställningar"</string> - <string name="privacy_sources_summary" msgid="2165270848857537278">"Behörigheter, inställningar"</string> + <string name="privacy_sources_summary" msgid="2165270848857537278">"Behörigheter och inställningar"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"Privat rum"</string> + <string name="private_space_title" msgid="6158245041481535879">"Privat utrymme"</string> <string name="private_space_summary" msgid="529869826714610294">"Ställ in privat rum med mera"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"Privat rum"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"Privat utrymme"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-ta-v35/strings.xml b/SafetyCenter/Resources/res/values-ta-v35/strings.xml index 8eb41ce20..0dd2252fd 100644 --- a/SafetyCenter/Resources/res/values-ta-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-ta-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"தனியுரிமைக் கட்டுப்பாடுகள்"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"அனுமதிகள், கட்டுப்பாடுகள்"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"தனிப்பட்ட சேமிப்பிடம்"</string> + <string name="private_space_title" msgid="6158245041481535879">"ரகசிய இடம்"</string> <string name="private_space_summary" msgid="529869826714610294">"தனிப்பட்ட சேமிப்பிடத்தை அமைக்கலாம் மற்றும் பலவற்றைச் செய்யலாம்"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"தனிப்பட்ட சேமிப்பிடம்"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"ரகசிய இடம்"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-tr-v35/strings.xml b/SafetyCenter/Resources/res/values-tr-v35/strings.xml index 89a3a3e0c..038bd9680 100644 --- a/SafetyCenter/Resources/res/values-tr-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-tr-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"Gizlilik denetimleri"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"İzinler, denetimler"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"Gizli Alan"</string> + <string name="private_space_title" msgid="6158245041481535879">"Özel Alan"</string> <string name="private_space_summary" msgid="529869826714610294">"Gizli alan yapılandırma ve daha fazlası"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"Gizli Alan"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"Özel Alan"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-ur-v35/strings.xml b/SafetyCenter/Resources/res/values-ur-v35/strings.xml index d4184bda3..d21e73368 100644 --- a/SafetyCenter/Resources/res/values-ur-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-ur-v35/strings.xml @@ -24,7 +24,7 @@ <string name="privacy_sources_title" msgid="309304028326660956">"رازداری سے متعلق کنٹرولز"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"اجازتیں، کنٹرولز"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> - <string name="private_space_title" msgid="6158245041481535879">"نجی اسپیس"</string> + <string name="private_space_title" msgid="6158245041481535879">"پرائیویٹ اسپیس"</string> <string name="private_space_summary" msgid="529869826714610294">"نجی اسپیس اور بھی بہت کچھ سیٹ اپ کریں"</string> - <string name="private_space_search_terms" msgid="4820808478299116258">"نجی اسپیس"</string> + <string name="private_space_search_terms" msgid="4820808478299116258">"پرائیویٹ اسپیس"</string> </resources> diff --git a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml index 54bbf0f1c..dc90b3fc3 100644 --- a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml +++ b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml @@ -18,7 +18,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="lock_screen_sources_title" msgid="5493678510117489865">"裝置解鎖"</string> - <string name="biometrics_title_for_work" msgid="1842284049407771568">"工作應用程式的生物特徵辨識選項"</string> + <string name="biometrics_title_for_work" msgid="1842284049407771568">"工作應用程式的生物辨識選項"</string> <string name="privacy_sources_summary" msgid="4083646673569677049">"權限、資訊主頁、控制選項"</string> <string name="health_connect_title" msgid="8318152190040327804">"健康資料同步"</string> <string name="health_connect_search_terms" msgid="4998970586245680829">"健康, 健康資料同步"</string> diff --git a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml index 5c44eb33a..6e6e41a38 100644 --- a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml +++ b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml @@ -23,7 +23,7 @@ <string name="lock_screen_title" msgid="4069104894527169877">"螢幕鎖定"</string> <string name="lock_screen_summary_disabled" msgid="354071230916616692">"目前還沒有任何資訊"</string> <string name="lock_screen_search_terms" msgid="2678486357779794826">"裝置鎖定,螢幕鎖定,將螢幕鎖定,鎖定螢幕,密碼,PIN 碼,圖案"</string> - <string name="biometrics_title" msgid="5859504610285212938">"生物特徵辨識"</string> + <string name="biometrics_title" msgid="5859504610285212938">"生物辨識"</string> <string name="biometrics_search_terms" msgid="6040319118762671981">"指紋,手指,新增指紋,人臉解鎖,人臉"</string> <string name="privacy_sources_title" msgid="4061110826457365957">"隱私權"</string> <string name="privacy_sources_summary" msgid="4089719981155120864">"資訊主頁、權限、控制選項"</string> diff --git a/TEST_MAPPING b/TEST_MAPPING index 524d02af5..823738181 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -16,12 +16,7 @@ ], "carpermission-postsubmit" : [ { - "name" : "CtsPermissionUiTestCases", - "options": [ - { - "include-annotation": "androidx.test.filters.FlakyTest" - } - ] + "name" : "CtsPermissionUiTestCases" } ], "alltests" : [ diff --git a/flags/Android.bp b/flags/Android.bp index 457aa4f91..d22da26c3 100644 --- a/flags/Android.bp +++ b/flags/Android.bp @@ -33,9 +33,10 @@ java_aconfig_library { sdk_version: "module_current", min_sdk_version: "30", installable: false, - libs: ["framework-configinfrastructure"], + libs: ["framework-configinfrastructure.stubs.module_lib"], visibility: [ "//packages/modules/Permission:__subpackages__", + "//vendor:__subpackages__", ], apex_available: [ "com.android.permission", @@ -49,7 +50,7 @@ java_aconfig_library { sdk_version: "module_current", min_sdk_version: "30", installable: false, - libs: ["framework-configinfrastructure"], + libs: ["framework-configinfrastructure.stubs.module_lib"], visibility: [ "//packages/modules/Permission:__subpackages__", ], diff --git a/flags/flags.aconfig b/flags/flags.aconfig index a81de8144..4cb084988 100644 --- a/flags/flags.aconfig +++ b/flags/flags.aconfig @@ -45,3 +45,111 @@ flag { bug: "327489942" is_fixed_read_only: true } + +flag { + name: "enhanced_confirmation_backport_enabled" + is_exported: true + namespace: "permissions" + description: "Flag to backport enhanced confirmation in permission mainline to T and U." + bug: "347876543" + is_fixed_read_only: true +} + +flag { + name: "enable_coarse_fine_location_prompt_for_aaos" + is_exported: true + namespace: "permissions" + description: "Feature flag to enable downgrading to coarse location on Automotive" + bug: "346369736" + is_fixed_read_only: true +} + +flag { + name: "livedata_refactor_permission_timeline_enabled" + is_exported: true + namespace: "permissions" + description: "This flag is used to enable modern app architecture implementation for timeline page" + bug: "354234946" + is_fixed_read_only: true +} + +flag { + name: "odad_notifications_supported" + is_exported: true + namespace: "permissions" + description: "This flag is used to enable Safety Center notifications support for ODAD" + bug: "356910008" + is_fixed_read_only: true +} + +flag { + name: "safety_center_issue_only_affects_group_status" + is_exported: true + namespace: "permissions" + description: "This flag is used by Safety Center to affect the status light of an entry group when an issue only source pushes a warning" + bug: "356910111" + is_fixed_read_only: true +} + +flag { + name: "safety_center_enabled_no_device_config" + is_exported: true + namespace: "permissions" + description: "This flag disables the usage of DeviceConfig to enable/disable Safety Center on UDC+ (enabled by default instead)" + bug: "354670125" + is_fixed_read_only: true +} + +flag { + name: "app_permission_fragment_uses_preferences" + is_exported: true + namespace: "permissions" + description: "This flag enables AppPermissionFragment rather than LegacyAppPermissionFragment" + bug: "349675008" + is_fixed_read_only: true +} + +flag { + name: "cross_user_role_enabled" + is_exported: true + namespace: "permissions" + description: "This flag enables cross-user roles support and API" + bug: "367732307" + is_fixed_read_only: true +} + +flag { + name: "permission_timeline_attribution_label_fix" + is_exported: true + namespace: "permissions" + description: "This flag is used for the attribution label fix on permission timeline page" + bug: "369606734" + is_fixed_read_only: true +} + +flag { + name: "wear_compose_material3" + is_exported: true + namespace: "permissions" + description: "This flag enables material3 design system for wear ui components" + bug: "370489422" + is_fixed_read_only: true +} + +flag { + name: "decluttered_permission_manager_enabled" + is_exported: true + namespace: "permissions" + description: "Enables displaying unused permission groups in the additional page, instead of displaying them in the main permission manager page" + bug: "365823624" + is_fixed_read_only: true +} + +flag { + name: "expressive_design_enabled" + is_exported: true + namespace: "permissions" + description: "This flag is used to enable Expressive Design for Settings pages inside PermissionController" + bug: "375480184" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index e15887576..fc06d6927 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -37,7 +37,9 @@ package android.app.role { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @Nullable @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, android.Manifest.permission.MANAGE_ROLE_HOLDERS, android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, conditional=true) public android.os.UserHandle getActiveUserForRole(@NonNull String); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public String getDefaultApplication(@NonNull String); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getDefaultHoldersForTest(@NonNull String); method @Deprecated @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); @@ -45,16 +47,21 @@ package android.app.role { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isBypassingRoleQualification(); method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleVisibleForTest(@NonNull String); method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, android.Manifest.permission.MANAGE_ROLE_HOLDERS, android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, conditional=true) public void setActiveUserForRole(@NonNull String, @NonNull android.os.UserHandle, int); method @RequiresPermission(android.Manifest.permission.BYPASS_ROLE_QUALIFICATION) public void setBypassingRoleQualification(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public void setDefaultApplication(@NonNull String, @Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setDefaultHoldersForTest(@NonNull String, @Nullable java.util.List<java.lang.String>); method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean); method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); + method @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleVisibleForTest(@NonNull String, boolean); 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 @FlaggedApi("com.android.permission.flags.cross_user_role_enabled") public static final String ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY = "android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY"; field public static final String ROLE_SYSTEM_ACTIVITY_RECOGNIZER = "android.app.role.SYSTEM_ACTIVITY_RECOGNIZER"; field public static final String ROLE_SYSTEM_CALL_STREAMING = "android.app.role.SYSTEM_CALL_STREAMING"; field public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION"; diff --git a/framework-s/jarjar-rules.txt b/framework-s/jarjar-rules.txt index da8f5a5f1..780410b7b 100644 --- a/framework-s/jarjar-rules.txt +++ b/framework-s/jarjar-rules.txt @@ -1,8 +1,12 @@ rule android.os.HandlerExecutor android.permission.jarjar.@0 -rule android.permission.flags.*Flags* android.permission.jarjar.@0 +rule android.permission.flags.*FeatureFlags* android.permission.jarjar.@0 +rule android.permission.flags.FeatureFlags* android.permission.jarjar.@0 +rule android.permission.flags.FeatureFlags android.permission.jarjar.@0 rule android.permission.flags.Flags android.permission.jarjar.@0 rule android.util.IndentingPrintWriter android.permission.jarjar.@0 rule com.android.internal.** android.permission.jarjar.@0 rule com.android.modules.** android.permission.jarjar.@0 -rule com.android.permission.flags.*Flags* android.permission.jarjar.@0 +rule com.android.permission.flags.*FeatureFlags* android.permission.jarjar.@0 +rule com.android.permission.flags.FeatureFlags* android.permission.jarjar.@0 +rule com.android.permission.flags.FeatureFlags android.permission.jarjar.@0 rule com.android.permission.flags.Flags android.permission.jarjar.@0 diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java index 74062165e..db05a0af6 100644 --- a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java +++ b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java @@ -20,6 +20,7 @@ import static android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_AC import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; @@ -35,15 +36,13 @@ import android.os.RemoteException; import android.permission.flags.Flags; import android.util.ArraySet; -import androidx.annotation.NonNull; - import java.lang.annotation.Retention; /** * This class provides the core API for ECM (Enhanced Confirmation Mode). ECM is a feature that * restricts access to protected **settings** (i.e., sensitive resources) by restricted **apps** * (apps from from dangerous sources, such as sideloaded packages or packages downloaded from a web - * browser). + * browser), or restricts settings globally based on device state. * * <p>Specifically, this class provides the ability to: * @@ -71,6 +70,9 @@ import java.lang.annotation.Retention; * particular app restricted is an implementation detail of ECM. However, the user is able to * clear any restricted app's restriction status (i.e, un-restrict it), after which ECM will * consider the app **not restricted**. + * <li>A setting may be globally restricted based on device state. In this case, any app may be + * automatically considered *restricted*, regardless of the app's restriction state. Users + * cannot un-restrict the app, in these cases. * </ol> * * Why is ECM needed? Consider the following (pre-ECM) scenario: @@ -313,6 +315,9 @@ public final class EnhancedConfirmationManager { * <p>This should be called from the "Restricted setting" dialog (which {@link * #createRestrictedSettingDialogIntent} directs to) upon being presented to the user. * + * <p>This restriction clearing does not apply to any settings that are restricted based on + * global device state + * * @param packageName package name of the application which should be considered acknowledged * @throws NameNotFoundException if the provided package was not found */ diff --git a/framework-s/java/android/app/role/IRoleManager.aidl b/framework-s/java/android/app/role/IRoleManager.aidl index 522967630..dc5bc8cb4 100644 --- a/framework-s/java/android/app/role/IRoleManager.aidl +++ b/framework-s/java/android/app/role/IRoleManager.aidl @@ -45,6 +45,10 @@ interface IRoleManager { void setDefaultApplicationAsUser(in String roleName, in String packageName, int flags, int userId, in RemoteCallback callback); + int getActiveUserForRoleAsUser(in String roleName, int userId); + + void setActiveUserForRoleAsUser(in String roleName, int activeUserId, int flags, int userId); + void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId); void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, @@ -80,4 +84,12 @@ interface IRoleManager { boolean isApplicationVisibleForRoleAsUser(in String roleName, in String packageName, int userId); + + List<String> getDefaultHoldersForTest(in String roleName); + + void setDefaultHoldersForTest(in String roleName, in List<String> packageNames); + + boolean isRoleVisibleForTest(in String roleName); + + void setRoleVisibleForTest(in String roleName, boolean visible); } diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java index 4b8c9b388..42445b4d6 100644 --- a/framework-s/java/android/app/role/RoleManager.java +++ b/framework-s/java/android/app/role/RoleManager.java @@ -40,6 +40,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.permission.flags.Flags; +import android.permission.internal.compat.UserHandleCompat; import android.util.ArrayMap; import android.util.SparseArray; @@ -211,6 +212,16 @@ public final class RoleManager { "android.app.role.SYSTEM_CALL_STREAMING"; /** + * The name of the role used for testing cross-user roles. + * + * @hide + */ + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SystemApi + public static final String ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY = + "android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY"; + + /** * @hide */ @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP }) @@ -574,6 +585,87 @@ public final class RoleManager { } } + /** + * Get the {@link UserHandle} of the user who that is the active user for the specified role. + * <p> + * Only profile-group exclusive roles can be used with this method, and they will + * have one active user within a profile group. + * <p> + * <strong>Note:</strong> Using this API requires holding + * {@code android.permission.INTERACT_ACROSS_USERS_FULL} and one of + * {@code android.permission.MANAGE_ROLE_HOLDERS} or + * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}. + * + * @param roleName the name of the role to get the active user for + * + * @return a {@link UserHandle} of the active user for the specified role + * + * @see #setActiveUserForRole(String, UserHandle, int) + * + * @hide + */ + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.MANAGE_ROLE_HOLDERS, + Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, + conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @Nullable + public UserHandle getActiveUserForRole(@NonNull String roleName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + try { + int userId = mService.getActiveUserForRoleAsUser(roleName, + mContext.getUser().getIdentifier()); + return userId == UserHandleCompat.USER_NULL ? null : UserHandle.of(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set a specific user as active user for a role. + * <p> + * Only profile-group exclusive roles can be used with this method, and they will have + * one active user within a profile group. + * <p> + * <strong>Note:</strong> Using this API requires holding + * {@code android.permission.INTERACT_ACROSS_USERS_FULL} and one of + * {@code android.permission.MANAGE_ROLE_HOLDERS} or + * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}. + * + * @param roleName the name of the role to set the active user for + * @param user the user to set as active user for specified role + * @param flags optional behavior flags + * + * @see #getActiveUserForRole(String) + * + * @hide + */ + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.MANAGE_ROLE_HOLDERS, + Manifest.permission.MANAGE_DEFAULT_APPLICATIONS}, + conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + // The user handle parameter is a value to be set by this method, while the context user of the + // operation is indeed read from the context + @SuppressLint("UserHandle") + public void setActiveUserForRole( + @NonNull String roleName, @NonNull UserHandle user, @ManageHoldersFlags int flags) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Objects.requireNonNull(user, "user cannot be null"); + try { + mService.setActiveUserForRoleAsUser(roleName, user.getIdentifier(), flags, + mContext.getUser().getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @NonNull private static RemoteCallback createRemoteCallback(@NonNull Executor executor, @NonNull Consumer<Boolean> callback) { @@ -1074,6 +1166,118 @@ public final class RoleManager { } } + /** + * Get the default holders of this role, which will be added when the role is added for the + * first time. + * <p> + * <strong>Note:</strong> Use of this API should be limited to tests. The values returned are + * not persisted. + * <p> + * Throws {@link IllegalArgumentException} if role is not a test role + * + * @param roleName the name of the role to get test default holders for + * @return the list of package names of the default holders + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @NonNull + public List<String> getDefaultHoldersForTest(@NonNull String roleName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + try { + return mService.getDefaultHoldersForTest(roleName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the default holders of this role, which will be added when the role is added for the + * first time. + * <p> + * <strong>Note:</strong> Use of this API should be limited to tests. The values used are + * not persisted. + * <p> + * Throws {@link IllegalArgumentException} if role is not a test role + * + * @param roleName the name of the role to set test default holders for + * @param packageNames a list of package names of the default holders or {@code null} to unset + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + public void setDefaultHoldersForTest( + @NonNull String roleName, @Nullable List<String> packageNames) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + try { + mService.setDefaultHoldersForTest(roleName, packageNames); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get whether a role should be visible for testing. + * <p> + * <strong>Note:</strong> Use of this API should be limited to tests. The values returned are + * not persisted. + * <p> + * Throws {@link IllegalArgumentException} if role is not a test role + * + * @param roleName the name of the role to get test visibility for + * @return {@code true} if role is visible, {@code false} otherwise + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + public boolean isRoleVisibleForTest(@NonNull String roleName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + try { + return mService.isRoleVisibleForTest(roleName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether a role should be visible for testing. + * <p> + * <strong>Note:</strong> Use of this API should be limited to tests. The values used are + * not persisted. + * <p> + * Throws {@link IllegalArgumentException} if role is not a test role + * + * @param roleName the name of the role to set test visibility for + * @param visible {@code true} to set role as visible, {@code false} otherwise + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @UserHandleAware + @FlaggedApi(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + public void setRoleVisibleForTest(@NonNull String roleName, boolean visible) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + try { + mService.setRoleVisibleForTest(roleName, visible); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @NonNull private RoleControllerManager getRoleControllerManager() { synchronized (mRoleControllerManagerLock) { diff --git a/service/java/com/android/permission/compat/UserHandleCompat.java b/framework-s/java/android/permission/internal/compat/UserHandleCompat.java index 1901aa997..8a3ec444d 100644 --- a/service/java/com/android/permission/compat/UserHandleCompat.java +++ b/framework-s/java/android/permission/internal/compat/UserHandleCompat.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permission.compat; +package android.permission.internal.compat; import android.annotation.UserIdInt; import android.os.UserHandle; @@ -29,6 +29,13 @@ public final class UserHandleCompat { public static final int USER_ALL = UserHandle.ALL.getIdentifier(); /** + * A user ID to indicate an undefined user of the device. + * + * @see UserHandle#USER_NULL + */ + public static final @UserIdInt int USER_NULL = -10000; + + /** * A user ID to indicate the "system" user of the device. */ public static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier(); diff --git a/service/java/com/android/permission/compat/package-info.java b/framework-s/java/android/permission/internal/compat/package-info.java index c89cc8eab..b78aac878 100644 --- a/service/java/com/android/permission/compat/package-info.java +++ b/framework-s/java/android/permission/internal/compat/package-info.java @@ -19,4 +19,4 @@ * TODO(b/146466118) remove this javadoc tag */ @android.annotation.Hide -package com.android.permission.compat; +package android.permission.internal.compat; diff --git a/framework-s/lint-baseline-framework-permission-s.xml b/framework-s/lint-baseline-framework-permission-s.xml index 9cf68a5c7..053c4ed43 100644 --- a/framework-s/lint-baseline-framework-permission-s.xml +++ b/framework-s/lint-baseline-framework-permission-s.xml @@ -4,12 +4,12 @@ <issue id="FlaggedApi" message="Method `onGetLegacyFallbackDisabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `getLegacyFallbackDisabledRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)" - errorLine1=" List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1=" List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/modules/Permission/framework-s/java/android/app/role/RoleControllerService.java" - line="189" - column="60"/> + line="197" + column="64"/> </issue> <issue diff --git a/permissions/com.android.permissioncontroller.xml b/permissions/com.android.permissioncontroller.xml index 60484e24e..0af3644ff 100644 --- a/permissions/com.android.permissioncontroller.xml +++ b/permissions/com.android.permissioncontroller.xml @@ -35,5 +35,6 @@ <permission name="android.permission.START_TASKS_FROM_RECENTS" /> <permission name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" /> + <permission name="android.permission.WATCH_APPOPS" /> </privapp-permissions> </permissions> diff --git a/service/Android.bp b/service/Android.bp index d80fa4f0d..b6e85b0fc 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -82,7 +82,7 @@ java_sdk_library { ], libs: [ "androidx.annotation_annotation", - "framework-configinfrastructure", + "framework-configinfrastructure.stubs.module_lib", // 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", @@ -101,6 +101,7 @@ java_sdk_library { "modules-utils-backgroundthread", "modules-utils-build", "modules-utils-os", + // framework-permission-s already includes com.android.permission.flags-aconfig-java "role-controller", "safety-center-config", "safety-center-internal-data", diff --git a/service/api/system-server-current.txt b/service/api/system-server-current.txt index 30fbab484..a3db89370 100644 --- a/service/api/system-server-current.txt +++ b/service/api/system-server-current.txt @@ -1,4 +1,18 @@ // Signature format: 2.0 +package com.android.ecm { + + @FlaggedApi("android.permission.flags.enhanced_confirmation_in_call_apis_enabled") public class EnhancedConfirmationCallTrackerService extends android.telecom.InCallService { + ctor public EnhancedConfirmationCallTrackerService(); + } + + @FlaggedApi("android.permission.flags.enhanced_confirmation_in_call_apis_enabled") public interface EnhancedConfirmationManagerLocal { + method public void addOngoingCall(@NonNull android.telecom.Call); + method public void clearOngoingCalls(); + method public void removeOngoingCall(@NonNull String); + } + +} + package com.android.permission.persistence { public interface RuntimePermissionsPersistence { diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt index 3c5184ead..d9833ca56 100644 --- a/service/jarjar-rules.txt +++ b/service/jarjar-rules.txt @@ -1,11 +1,39 @@ -rule android.os.*Flags* com.android.permission.jarjar.@0 +# You may bypass this Gerrit IfThisThenThat Lint if your change doesn't affect +# RoleParser.applyJarjarTransform(), by adding NO_IFTTT=reason to your commit +# message. +# LINT.IfChange +rule android.app.admin.flags.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.app.admin.flags.FeatureFlags* com.android.permission.jarjar.@0 +rule android.app.admin.flags.FeatureFlags com.android.permission.jarjar.@0 +rule android.app.admin.flags.Flags com.android.permission.jarjar.@0 +rule android.app.appfunctions.flags.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.app.appfunctions.flags.FeatureFlags* com.android.permission.jarjar.@0 +rule android.app.appfunctions.flags.FeatureFlags com.android.permission.jarjar.@0 +rule android.app.appfunctions.flags.Flags com.android.permission.jarjar.@0 +rule android.companion.virtualdevice.flags.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.companion.virtualdevice.flags.FeatureFlags* com.android.permission.jarjar.@0 +rule android.companion.virtualdevice.flags.FeatureFlags com.android.permission.jarjar.@0 +rule android.companion.virtualdevice.flags.Flags com.android.permission.jarjar.@0 +rule android.content.pm.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.content.pm.FeatureFlags* com.android.permission.jarjar.@0 +rule android.content.pm.FeatureFlags com.android.permission.jarjar.@0 +rule android.content.pm.Flags com.android.permission.jarjar.@0 +rule android.os.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.os.FeatureFlags* com.android.permission.jarjar.@0 +rule android.os.FeatureFlags com.android.permission.jarjar.@0 rule android.os.Flags com.android.permission.jarjar.@0 rule android.os.HandlerExecutor com.android.permission.jarjar.@0 -rule android.permission.flags.*Flags* com.android.permission.jarjar.@0 +rule android.permission.flags.*FeatureFlags* com.android.permission.jarjar.@0 +rule android.permission.flags.FeatureFlags* com.android.permission.jarjar.@0 +rule android.permission.flags.FeatureFlags com.android.permission.jarjar.@0 rule android.permission.flags.Flags com.android.permission.jarjar.@0 rule android.util.IndentingPrintWriter com.android.permission.jarjar.@0 rule com.android.internal.** com.android.permission.jarjar.@0 rule com.android.modules.** com.android.permission.jarjar.@0 +rule com.android.permission.flags.*FeatureFlags* com.android.permission.jarjar.@0 +rule com.android.permission.flags.FeatureFlags* com.android.permission.jarjar.@0 +rule com.android.permission.flags.FeatureFlags com.android.permission.jarjar.@0 +rule com.android.permission.flags.Flags com.android.permission.jarjar.@0 rule com.android.role.*Proto com.android.permission.jarjar.@0 # TODO(b/236200992): Revisit addition of rule com.android.safetycenter.annotations, # com.android.safetycenter.internaldata, com.android.safetycenter.pendingintents and @@ -17,3 +45,4 @@ rule com.android.safetycenter.resources.** com.android.permission.jarjar.@0 rule com.google.protobuf.** com.android.permission.jarjar.@0 rule kotlin.** com.android.permission.jarjar.@0 rule com.android.permissioncontroller.PermissionControllerStatsLog com.android.permission.jarjar.@0 +# LINT.ThenChange(PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java:applyJarjarTransform) diff --git a/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java b/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java new file mode 100644 index 000000000..407d56f70 --- /dev/null +++ b/service/java/com/android/ecm/EnhancedConfirmationCallTrackerService.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ecm; + +import android.annotation.FlaggedApi; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TargetApi; +import android.content.Intent; +import android.os.Build; +import android.permission.flags.Flags; +import android.telecom.Call; +import android.telecom.InCallService; + +import com.android.server.LocalManagerRegistry; + +/** + * @hide + * + * This InCallService tracks called (both incoming and outgoing), and sends their information to the + * EnhancedConfirmationService + * + **/ +@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_IN_CALL_APIS_ENABLED) +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +@TargetApi(Build.VERSION_CODES.BAKLAVA) +public class EnhancedConfirmationCallTrackerService extends InCallService { + private EnhancedConfirmationManagerLocal mEnhancedConfirmationManagerLocal; + + @Override + public void onCreate() { + super.onCreate(); + if (Flags.enhancedConfirmationInCallApisEnabled()) { + mEnhancedConfirmationManagerLocal = + LocalManagerRegistry.getManager(EnhancedConfirmationManagerLocal.class); + } + } + + @Override + public void onCallAdded(@Nullable Call call) { + if (mEnhancedConfirmationManagerLocal == null || call == null) { + return; + } + + mEnhancedConfirmationManagerLocal.addOngoingCall(call); + } + + @Override + public void onCallRemoved(@Nullable Call call) { + if (mEnhancedConfirmationManagerLocal == null || call == null) { + return; + } + + mEnhancedConfirmationManagerLocal.removeOngoingCall(call.getDetails().getId()); + } + + /** + * When unbound, we should assume all calls have finished. Notify the system of such. + */ + public boolean onUnbind(@Nullable Intent intent) { + if (mEnhancedConfirmationManagerLocal != null) { + mEnhancedConfirmationManagerLocal.clearOngoingCalls(); + } + return super.onUnbind(intent); + } +} diff --git a/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java b/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java new file mode 100644 index 000000000..483071716 --- /dev/null +++ b/service/java/com/android/ecm/EnhancedConfirmationManagerLocal.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ecm; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TargetApi; +import android.os.Build; +import android.permission.flags.Flags; +import android.telecom.Call; + +/** + * @hide + * + * In-process API for the Enhanced Confirmation Service + */ +@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_IN_CALL_APIS_ENABLED) +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +@TargetApi(Build.VERSION_CODES.BAKLAVA) +public interface EnhancedConfirmationManagerLocal { + /** + * Inform the enhanced confirmation service of an ongoing call + * + * @param call The call to potentially track + * + */ + void addOngoingCall(@NonNull Call call); + + /** + * Inform the enhanced confirmation service that a call has ended + * + * @param callId The ID of the call to stop tracking + * + */ + void removeOngoingCall(@NonNull String callId); + + /** + * Informs the enhanced confirmation service it should clear out any ongoing calls + */ + void clearOngoingCalls(); +} diff --git a/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java b/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java new file mode 100644 index 000000000..a5c6d3c36 --- /dev/null +++ b/service/java/com/android/ecm/EnhancedConfirmationManagerLocalImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ecm; + +import android.annotation.NonNull; +import android.annotation.TargetApi; +import android.os.Build; +import android.permission.flags.Flags; +import android.telecom.Call; + +/** @hide */ +@TargetApi(Build.VERSION_CODES.BAKLAVA) +class EnhancedConfirmationManagerLocalImpl implements EnhancedConfirmationManagerLocal { + + private final EnhancedConfirmationService mService; + + EnhancedConfirmationManagerLocalImpl(EnhancedConfirmationService service) { + if (Flags.enhancedConfirmationInCallApisEnabled()) { + mService = service; + } else { + mService = null; + } + } + + @Override + public void addOngoingCall(@NonNull Call call) { + if (mService != null) { + mService.addOngoingCall(call); + } + } + + @Override + public void removeOngoingCall(@NonNull String callId) { + if (mService != null) { + mService.removeOngoingCall(callId); + } + } + + @Override + public void clearOngoingCalls() { + if (mService != null) { + mService.clearOngoingCalls(); + } + } +} diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java index 708884e85..515f70add 100644 --- a/service/java/com/android/ecm/EnhancedConfirmationService.java +++ b/service/java/com/android/ecm/EnhancedConfirmationService.java @@ -19,11 +19,15 @@ package com.android.ecm; import android.Manifest; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.ecm.EnhancedConfirmationManager; import android.app.ecm.IEnhancedConfirmationManager; import android.app.role.RoleManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.InstallSourceInfo; @@ -31,25 +35,33 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.SignedPackage; +import android.database.Cursor; +import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.SystemConfigManager; import android.os.UserHandle; import android.permission.flags.Flags; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.PhoneLookup; +import android.telecom.Call; +import android.telecom.PhoneAccount; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import androidx.annotation.Keep; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.android.internal.util.Preconditions; import com.android.permission.util.UserUtils; +import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -66,16 +78,35 @@ import java.util.Set; @Keep @FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@SuppressLint("MissingPermission") public class EnhancedConfirmationService extends SystemService { private static final String LOG_TAG = EnhancedConfirmationService.class.getSimpleName(); private Map<String, List<byte[]>> mTrustedPackageCertDigests; private Map<String, List<byte[]>> mTrustedInstallerCertDigests; + // A map of call ID to call type + private final Map<String, Integer> mOngoingCalls = new ArrayMap<>(); + + private static final int CALL_TYPE_UNTRUSTED = 0; + private static final int CALL_TYPE_TRUSTED = 1; + private static final int CALL_TYPE_EMERGENCY = 2; + @IntDef(flag = true, value = { + CALL_TYPE_UNTRUSTED, + CALL_TYPE_TRUSTED, + CALL_TYPE_EMERGENCY + }) + @Retention(RetentionPolicy.SOURCE) + @interface CallType {} public EnhancedConfirmationService(@NonNull Context context) { super(context); + LocalManagerRegistry.addManager(EnhancedConfirmationManagerLocal.class, + new EnhancedConfirmationManagerLocalImpl(this)); } + private ContentResolver mContentResolver; + private TelephonyManager mTelephonyManager; + @Override public void onStart() { Context context = getContext(); @@ -87,6 +118,8 @@ public class EnhancedConfirmationService extends SystemService { systemConfigManager.getEnhancedConfirmationTrustedInstallers()); publishBinderService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE, new Stub()); + mContentResolver = getContext().getContentResolver(); + mTelephonyManager = getContext().getSystemService(TelephonyManager.class); } private Map<String, List<byte[]>> toTrustedPackageMap(Set<SignedPackage> signedPackages) { @@ -99,6 +132,97 @@ public class EnhancedConfirmationService extends SystemService { return trustedPackageMap; } + void addOngoingCall(Call call) { + if (!Flags.enhancedConfirmationInCallApisEnabled()) { + return; + } + if (call.getDetails() == null) { + return; + } + mOngoingCalls.put(call.getDetails().getId(), getCallType(call)); + } + + void removeOngoingCall(String callId) { + if (!Flags.enhancedConfirmationInCallApisEnabled()) { + return; + } + Integer returned = mOngoingCalls.remove(callId); + if (returned == null) { + // TODO b/379941144: Capture a bug report whenever this happens. + } + } + + void clearOngoingCalls() { + mOngoingCalls.clear(); + } + + private @CallType int getCallType(Call call) { + String number = getPhoneNumber(call); + try { + if (number != null && mTelephonyManager.isEmergencyNumber(number)) { + return CALL_TYPE_EMERGENCY; + } + } catch (RuntimeException e) { + // If either of these are thrown, the telephony service is not available on the current + // device, either because the device lacks telephony calling, or the telephony service + // is unavailable. + } + if (number != null) { + return hasContactWithPhoneNumber(number) ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; + } else { + return hasContactWithDisplayName(call.getDetails().getCallerDisplayName()) + ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; + } + } + + private String getPhoneNumber(Call call) { + Uri handle = call.getDetails().getHandle(); + if (handle == null || handle.getScheme() == null) { + return null; + } + if (!handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { + return null; + } + return handle.getSchemeSpecificPart(); + } + + private boolean hasContactWithPhoneNumber(String phoneNumber) { + if (phoneNumber == null) { + return false; + } + Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(phoneNumber)); + String[] projection = new String[]{ + PhoneLookup.DISPLAY_NAME, + ContactsContract.PhoneLookup._ID + }; + try (Cursor res = mContentResolver.query(uri, projection, null, null)) { + return res != null && res.getCount() > 0; + } + } + + private boolean hasContactWithDisplayName(String displayName) { + if (displayName == null) { + return false; + } + Uri uri = ContactsContract.Data.CONTENT_URI; + String[] projection = new String[]{PhoneLookup._ID}; + String selection = StructuredName.DISPLAY_NAME + " = ?"; + String[] selectionArgs = new String[]{displayName}; + try (Cursor res = mContentResolver.query(uri, projection, selection, selectionArgs, null)) { + return res != null && res.getCount() > 0; + } + } + + private boolean hasCallOfType(@CallType int callType) { + for (int ongoingCallType : mOngoingCalls.values()) { + if (ongoingCallType == callType) { + return true; + } + } + return false; + } + private class Stub extends IEnhancedConfirmationManager.Stub { /** A map of ECM states to their corresponding app op states */ @@ -115,6 +239,10 @@ public class EnhancedConfirmationService extends SystemService { private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>(); + // Settings restricted when an untrusted call is ongoing. These must also be added to + // PROTECTED_SETTINGS + private static final ArraySet<String> UNTRUSTED_CALL_RESTRICTED_SETTINGS = new ArraySet<>(); + static { // Runtime permissions PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); @@ -135,6 +263,13 @@ public class EnhancedConfirmationService extends SystemService { // Default application roles. PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER); PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); + + if (Flags.unknownCallPackageInstallBlockingEnabled()) { + // Requesting package installs, limited during phone calls + PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); + UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( + AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); + } } private final @NonNull Context mContext; @@ -163,8 +298,13 @@ public class EnhancedConfirmationService extends SystemService { "settingIdentifier cannot be null or empty"); try { - return isSettingEcmProtected(settingIdentifier) && isPackageEcmGuarded(packageName, - userId); + if (!isSettingEcmProtected(settingIdentifier)) { + return false; + } + if (isSettingProtectedGlobally(settingIdentifier)) { + return true; + } + return isPackageEcmGuarded(packageName, userId); } catch (NameNotFoundException e) { throw new IllegalArgumentException(e); } @@ -227,8 +367,21 @@ public class EnhancedConfirmationService extends SystemService { } } + private boolean isUntrustedCallOngoing() { + if (!Flags.unknownCallPackageInstallBlockingEnabled()) { + return false; + } + + if (hasCallOfType(CALL_TYPE_EMERGENCY)) { + // If we have an emergency call, return false always. + return false; + } + return hasCallOfType(CALL_TYPE_UNTRUSTED); + } + private void enforcePermissions(@NonNull String methodName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, methodName, mContext); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, methodName, mContext); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES, methodName); } @@ -322,6 +475,7 @@ public class EnhancedConfirmationService extends SystemService { return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + @SuppressLint("WrongConstant") private void setAppEcmState(@NonNull String packageName, @EcmState int ecmState, @UserIdInt int userId) throws NameNotFoundException { int packageUid = getPackageUid(packageName, userId); @@ -359,6 +513,14 @@ public class EnhancedConfirmationService extends SystemService { return false; } + private boolean isSettingProtectedGlobally(@NonNull String settingIdentifier) { + if (UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier)) { + return isUntrustedCallOngoing(); + } + + return false; + } + @Nullable private ApplicationInfo getApplicationInfoAsUser(@Nullable String packageName, @UserIdInt int userId) { diff --git a/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java index f3ba5aaef..1fb0aaa2b 100644 --- a/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ b/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java @@ -214,9 +214,6 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers @Override public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user) { - File reserveFile = getReserveCopyFile(user); - reserveFile.delete(); - File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); FileOutputStream outputStream = null; @@ -240,6 +237,8 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers IoUtils.closeQuietly(outputStream); } + File reserveFile = getReserveCopyFile(user); + reserveFile.delete(); try (FileInputStream in = new FileInputStream(file); FileOutputStream out = new FileOutputStream(reserveFile)) { FileUtils.copy(in, out); diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java index 33389a88f..c69afb199 100644 --- a/service/java/com/android/permission/util/UserUtils.java +++ b/service/java/com/android/permission/util/UserUtils.java @@ -17,18 +17,21 @@ package com.android.permission.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.permission.internal.compat.UserHandleCompat; import com.android.internal.util.Preconditions; import com.android.modules.utils.build.SdkLevel; -import com.android.permission.compat.UserHandleCompat; import com.android.permission.flags.Flags; +import java.util.ArrayList; import java.util.List; /** Utility class to deal with Android users. */ @@ -40,11 +43,12 @@ public final class UserUtils { public static void enforceCrossUserPermission( @UserIdInt int userId, boolean allowAll, + boolean enforceForProfileGroup, @NonNull String message, @NonNull Context context) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandleCompat.getUserId(callingUid); - if (userId == callingUserId) { + if (userId == callingUserId && !enforceForProfileGroup) { return; } Preconditions.checkArgument( @@ -53,13 +57,40 @@ public final class UserUtils { "Invalid user " + userId); context.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - if (callingUid != Process.SHELL_UID || userId < UserHandleCompat.USER_SYSTEM) { + if (callingUid != Process.SHELL_UID || userId == UserHandleCompat.USER_ALL) { return; } + + if (enforceForProfileGroup) { + DevicePolicyManager devicePolicyManager = + context.getSystemService(DevicePolicyManager.class); + if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { + // For profileGroup exclusive roles users on BYOD are free to choose personal o + // work profile app regardless of DISALLOW_DEBUGGING_FEATURES + return; + } + + Context userContext = UserUtils.getUserContext(userId, context); + List<UserHandle> profiles = getUserProfiles(userContext, true); + final int profilesSize = profiles.size(); + for (int i = 0; i < profilesSize; i++) { + int profileId = profiles.get(i).getIdentifier(); + if (profileId == callingUserId) { + continue; + } + enforceShellRestriction(profileId, context); + } + } else { + enforceShellRestriction(userId, context); + } + } + + private static void enforceShellRestriction(int userId, @NonNull Context context) { UserManager userManager = context.getSystemService(UserManager.class); if (userManager.hasUserRestrictionForUser( UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId))) { - throw new SecurityException("Shell does not have permission to access user " + userId); + throw new SecurityException( + "Shell does not have permission to access user " + userId); } } @@ -81,6 +112,68 @@ public final class UserUtils { } } + /** Returns all the enabled user profiles on the device. */ + @NonNull + public static List<UserHandle> getUserProfiles(@NonNull Context context) { + return getUserProfiles(context, false); + } + + /** + * Returns all the enabled user profiles on the device + * + * @param context the {@link Context} + * @param excludePrivate {@code true} to exclude private profiles from returned list of users + */ + @NonNull + public static List<UserHandle> getUserProfiles(@NonNull Context context, + boolean excludePrivate) { + UserManager userManager = context.getSystemService(UserManager.class); + // This call requires the QUERY_USERS permission. + final long identity = Binder.clearCallingIdentity(); + try { + List<UserHandle> profiles = userManager.getUserProfiles(); + if (!excludePrivate) { + return profiles; + } + List<UserHandle> filteredProfiles = new ArrayList<>(); + final int profilesSize = profiles.size(); + for (int i = 0; i < profilesSize; i++) { + UserHandle user = profiles.get(i); + if (!isPrivateProfile(user.getIdentifier(), context)) { + filteredProfiles.add(user); + } + } + return filteredProfiles; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Returns the parent of a given user, or userId if it has no parent (e.g. it is the primary + * profile) + */ + @UserIdInt + public static int getProfileParentIdOrSelf(@UserIdInt int userId, @NonNull Context context) { + UserHandle profileParent = getProfileParent(userId, context); + // If profile parent userhandle is null, then original user id is the parent + return profileParent != null ? profileParent.getIdentifier() : userId; + } + + /** Returns the parent of a given user. */ + @Nullable + private static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) { + Context userContext = getUserContext(userId, context); + UserManager userManager = userContext.getSystemService(UserManager.class); + // This call requires the INTERACT_ACROSS_USERS permission. + final long identity = Binder.clearCallingIdentity(); + try { + return userManager.getProfileParent(UserHandle.of(userId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + /** Returns whether a given {@code userId} corresponds to a managed profile. */ public static boolean isManagedProfile(@UserIdInt int userId, @NonNull Context context) { UserManager userManager = context.getSystemService(UserManager.class); @@ -107,8 +200,7 @@ public final class UserUtils { // MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS. final long identity = Binder.clearCallingIdentity(); try { - Context userContext = context - .createContextAsUser(UserHandle.of(userId), /* flags= */ 0); + Context userContext = getUserContext(userId, context); UserManager userManager = userContext.getSystemService(UserManager.class); return userManager != null && userManager.isPrivateProfile(); } finally { @@ -141,4 +233,13 @@ public final class UserUtils { Binder.restoreCallingIdentity(identity); } } + + @NonNull + public static Context getUserContext(@UserIdInt int userId, @NonNull Context context) { + if (SdkLevel.isAtLeastS() && context.getUser().getIdentifier() == userId) { + return context; + } else { + return context.createContextAsUser(UserHandle.of(userId), 0); + } + } } diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java index 8348d4064..0fea33899 100644 --- a/service/java/com/android/role/RoleService.java +++ b/service/java/com/android/role/RoleService.java @@ -16,6 +16,8 @@ package com.android.role; +import static android.app.role.RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY; + import android.Manifest; import android.annotation.AnyThread; import android.annotation.MainThread; @@ -43,11 +45,14 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.permission.flags.Flags; +import android.permission.internal.compat.UserHandleCompat; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; @@ -62,13 +67,16 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.modules.utils.build.SdkLevel; -import com.android.permission.compat.UserHandleCompat; import com.android.permission.util.ArrayUtils; import com.android.permission.util.CollectionUtils; import com.android.permission.util.ForegroundThread; import com.android.permission.util.PackageUtils; import com.android.permission.util.ThrottledRunnable; import com.android.permission.util.UserUtils; +import com.android.role.controller.model.Role; +import com.android.role.controller.model.Roles; +import com.android.role.controller.service.RoleControllerServiceImpl; +import com.android.role.controller.util.RoleFlags; import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; import com.android.server.role.RoleServicePlatformHelper; @@ -77,6 +85,7 @@ import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -95,6 +104,7 @@ import java.util.concurrent.TimeoutException; @RequiresApi(Build.VERSION_CODES.S) public class RoleService extends SystemService implements RoleUserState.Callback { private static final String LOG_TAG = RoleService.class.getSimpleName(); + private static final String TRACE_TAG = RoleService.class.getSimpleName(); private static final boolean DEBUG = false; @@ -114,9 +124,22 @@ public class RoleService extends SystemService implements RoleUserState.Callback if (SdkLevel.isAtLeastV()) { defaultApplicationRoles.add(RoleManager.ROLE_WALLET); } + if (RoleFlags.isProfileGroupExclusivityAvailable()) { + defaultApplicationRoles.add(ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY); + } DEFAULT_APPLICATION_ROLES = defaultApplicationRoles.toArray(new String[0]); } + private static final String[] TEST_ROLES; + + static { + if (RoleFlags.isProfileGroupExclusivityAvailable()) { + TEST_ROLES = new String[] {ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY}; + } else { + TEST_ROLES = new String[0]; + } + } + @NonNull private final AppOpsManager mAppOpsManager; @@ -162,9 +185,22 @@ public class RoleService extends SystemService implements RoleUserState.Callback private final SparseArray<ThrottledRunnable> mGrantDefaultRolesThrottledRunnables = new SparseArray<>(); + @GuardedBy("mLock") + @NonNull + private final Map<String, List<String>> mDefaultHoldersForTest = new ArrayMap<>(); + + @GuardedBy("mLock") + @NonNull + private final Set<String> mRolesVisibleForTest = new ArraySet<>(); + public RoleService(@NonNull Context context) { super(context); + if (RoleFlags.isProfileGroupExclusivityAvailable()) { + RoleControllerServiceImpl.sSetActiveUserForRoleMethod = + this::setActiveUserForRoleFromController; + } + mPlatformHelper = LocalManagerRegistry.getManager(RoleServicePlatformHelper.class); RoleControllerManager.initializeRemoteServiceComponentName(context); @@ -176,6 +212,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback registerUserRemovedReceiver(); } + // TODO(b/375029649): enforce single active user for all cross-user roles private void registerUserRemovedReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_REMOVED); @@ -191,6 +228,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback }, intentFilter, null, null); } + // TODO(b/375029649): enforce single active user for all cross-user roles @Override public void onStart() { publishBinderService(Context.ROLE_SERVICE, new Stub()); @@ -259,11 +297,16 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public void onUserStarting(@NonNull TargetUser user) { - if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { - upgradeLegacyFallbackEnabledRolesIfNeeded(user); - } + Trace.beginSection(TRACE_TAG + "_onUserStarting"); + try { + if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { + upgradeLegacyFallbackEnabledRolesIfNeeded(user); + } - maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier()); + maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier()); + } finally { + Trace.endSection(); + } } private void upgradeLegacyFallbackEnabledRolesIfNeeded(@NonNull TargetUser user) { @@ -276,6 +319,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback if (legacyFallbackDisabledRoles == null) { return; } + Log.v(LOG_TAG, "Received legacy fallback disabled roles: " + legacyFallbackDisabledRoles); userState.upgradeVersion(legacyFallbackDisabledRoles); } @@ -459,12 +503,100 @@ public class RoleService extends SystemService implements RoleUserState.Callback } } + private void enforceProfileGroupExclusiveRole(@NonNull String roleName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkArgument(isProfileGroupExclusiveRole(roleName, getContext()), + roleName + " is not a profile-group exclusive role"); + } + + /** + * Returns whether the given role has profile group exclusivity + * + * @param roleName The name of role to check + * @param context The context + * @return {@code true} if the role is profile group exclusive, {@code false} otherwise + */ + private static boolean isProfileGroupExclusiveRole(String roleName, Context context) { + if (!RoleFlags.isProfileGroupExclusivityAvailable()) { + return false; + } + Role role = Roles.get(context).get(roleName); + return role != null && role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP; + } + + private void setActiveUserForRoleFromController(@NonNull String roleName, @UserIdInt int userId, + @RoleManager.ManageHoldersFlags int flags) { + setActiveUserForRoleAsUserInternal(roleName, userId, flags, false, userId); + } + + private void setActiveUserForRoleAsUserInternal(@NonNull String roleName, + @UserIdInt int activeUserId, @RoleManager.ManageHoldersFlags int flags, + boolean clearRoleHoldersForActiveUser, @UserIdInt int userId) { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "setActiveUserForRoleAsUser not available"); + enforceProfileGroupExclusiveRole(roleName); + + if (!UserUtils.isUserExistent(userId, getContext())) { + Log.e(LOG_TAG, "user " + userId + " does not exist"); + return; + } + if (!UserUtils.isUserExistent(activeUserId, getContext())) { + Log.e(LOG_TAG, "user " + activeUserId + " does not exist"); + return; + } + if (UserUtils.isPrivateProfile(activeUserId, getContext())) { + Log.e(LOG_TAG, "Cannot set private profile " + activeUserId + " as active user" + + " for role"); + return; + } + Context userContext = UserUtils.getUserContext(userId, getContext()); + List<UserHandle> profiles = UserUtils.getUserProfiles(userContext, true); + if (!profiles.contains(UserHandle.of(activeUserId))) { + Log.e(LOG_TAG, "User " + activeUserId + " is not in the same profile-group as " + + userId); + return; + } + + int profileParentId = UserUtils.getProfileParentIdOrSelf(userId, getContext()); + RoleUserState userState = getOrCreateUserState(profileParentId); + + if (!userState.setActiveUserForRole(roleName, activeUserId)) { + Log.i(LOG_TAG, "User " + activeUserId + " is already the active user for role"); + return; + } + + final int profilesSize = profiles.size(); + for (int i = 0; i < profilesSize; i++) { + int profilesUserId = profiles.get(i).getIdentifier(); + if (!clearRoleHoldersForActiveUser && profilesUserId == activeUserId) { + continue; + } + final AndroidFuture<Void> future = new AndroidFuture<>(); + final RemoteCallback callback = new RemoteCallback(result -> { + boolean successful = result != null; + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException()); + } + }); + getOrCreateController(profilesUserId) + .onClearRoleHolders(roleName, flags, callback); + try { + future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Log.e(LOG_TAG, "Exception while clearing role holders for non-active user: " + + profilesUserId, e); + } + } + } + private class Stub extends IRoleManager.Stub { @Override public boolean isRoleAvailableAsUser(@NonNull String roleName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "isRoleAvailableAsUser", - getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "isRoleAvailableAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -480,7 +612,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @UserIdInt int userId) { mAppOpsManager.checkPackage(getCallingUid(), packageName); - UserUtils.enforceCrossUserPermission(userId, false, "isRoleHeldAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "isRoleHeldAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -499,8 +632,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @NonNull @Override public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser", - getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "getRoleHoldersAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return Collections.emptyList(); @@ -522,8 +655,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @NonNull RemoteCallback callback) { - UserUtils.enforceCrossUserPermission(userId, false, "addRoleHolderAsUser", - getContext()); + boolean enforceForProfileGroup = isProfileGroupExclusiveRole(roleName, getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + enforceForProfileGroup, "addRoleHolderAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return; @@ -543,8 +677,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @NonNull RemoteCallback callback) { - UserUtils.enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser", - getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "removeRoleHolderAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return; @@ -565,8 +699,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback public void clearRoleHoldersAsUser(@NonNull String roleName, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @NonNull RemoteCallback callback) { - UserUtils.enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser", - getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "clearRoleHoldersAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return; @@ -584,7 +718,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override @Nullable public String getDefaultApplicationAsUser(@NonNull String roleName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "getDefaultApplicationAsUser", + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "getDefaultApplicationAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); @@ -609,8 +744,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback public void setDefaultApplicationAsUser(@NonNull String roleName, @Nullable String packageName, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @NonNull RemoteCallback callback) { - UserUtils.enforceCrossUserPermission(userId, false, "setDefaultApplicationAsUser", - getContext()); + boolean enforceForProfileGroup = isProfileGroupExclusiveRole(roleName, getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + enforceForProfileGroup, "setDefaultApplicationAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return; @@ -632,10 +768,59 @@ public class RoleService extends SystemService implements RoleUserState.Callback } @Override + public int getActiveUserForRoleAsUser(@NonNull String roleName, @UserIdInt int userId) { + Trace.beginSection(TRACE_TAG + "_getActiveUserForRoleAsUser"); + try { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "getActiveUserForRoleAsUser not available"); + enforceProfileGroupExclusiveRole(roleName); + + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ true, "getActiveUserForRole", getContext()); + if (!UserUtils.isUserExistent(userId, getContext())) { + Log.e(LOG_TAG, "user " + userId + " does not exist"); + return UserHandleCompat.USER_NULL; + } + + enforceCallingOrSelfAnyPermissions(new String[] { + Manifest.permission.MANAGE_DEFAULT_APPLICATIONS, + Manifest.permission.MANAGE_ROLE_HOLDERS + }, "getActiveUserForRole"); + + int profileParentId = UserUtils.getProfileParentIdOrSelf(userId, getContext()); + RoleUserState userState = getOrCreateUserState(profileParentId); + return userState.getActiveUserForRole(roleName); + } finally { + Trace.endSection(); + } + } + + @Override + public void setActiveUserForRoleAsUser(@NonNull String roleName, + @UserIdInt int activeUserId, @RoleManager.ManageHoldersFlags int flags, + @UserIdInt int userId) { + Trace.beginSection(TRACE_TAG + "_setActiveUserForRoleAsUser"); + try { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "setActiveUserForRoleAsUser not available"); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ true, "setActiveUserForRole", getContext()); + enforceCallingOrSelfAnyPermissions(new String[] { + Manifest.permission.MANAGE_DEFAULT_APPLICATIONS, + Manifest.permission.MANAGE_ROLE_HOLDERS + }, "setActiveUserForRoleAsUser"); + setActiveUserForRoleAsUserInternal(roleName, activeUserId, flags, true, userId); + } finally { + Trace.endSection(); + } + } + + @Override public void addOnRoleHoldersChangedListenerAsUser( @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, true, - "addOnRoleHoldersChangedListenerAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ true, + /* enforceForProfileGroup= */ false, "addOnRoleHoldersChangedListenerAsUser", + getContext()); if (userId != UserHandleCompat.USER_ALL && !UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); @@ -655,7 +840,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public void removeOnRoleHoldersChangedListenerAsUser( @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, true, + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ true, + /* enforceForProfileGroup= */ false, "removeOnRoleHoldersChangedListenerAsUser", getContext()); if (userId != UserHandleCompat.USER_ALL && !UserUtils.isUserExistent(userId, getContext())) { @@ -708,7 +894,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public boolean isRoleFallbackEnabledAsUser(@NonNull String roleName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "isRoleFallbackEnabledAsUser", + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "isRoleFallbackEnabledAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); @@ -726,7 +913,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public void setRoleFallbackEnabledAsUser(@NonNull String roleName, boolean fallbackEnabled, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "setRoleFallbackEnabledAsUser", + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "setRoleFallbackEnabledAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); @@ -744,7 +932,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public void setRoleNamesFromControllerAsUser(@NonNull List<String> roleNames, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "setRoleNamesFromControllerAsUser", + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "setRoleNamesFromControllerAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); @@ -763,8 +952,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public boolean addRoleHolderFromControllerAsUser(@NonNull String roleName, @NonNull String packageName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, - "addRoleHolderFromControllerAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "addRoleHolderFromControllerAsUser", + getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -783,8 +973,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public boolean removeRoleHolderFromControllerAsUser(@NonNull String roleName, @NonNull String packageName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, - "removeRoleHolderFromControllerAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "removeRoleHolderFromControllerAsUser", + getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -803,8 +994,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public List<String> getHeldRolesFromControllerAsUser(@NonNull String packageName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, - "getHeldRolesFromControllerAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "getHeldRolesFromControllerAsUser", + getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return Collections.emptyList(); @@ -913,7 +1105,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public String getSmsRoleHolder(int userId) { final Context context = getContext(); - UserUtils.enforceCrossUserPermission(userId, false, "getSmsRoleHolder", context); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "getSmsRoleHolder", context); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return null; @@ -937,7 +1130,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public String getEmergencyRoleHolder(int userId) { final Context context = getContext(); - UserUtils.enforceCrossUserPermission(userId, false, "getEmergencyRoleHolder", context); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "getEmergencyRoleHolder", context); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return null; @@ -963,8 +1157,8 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public boolean isRoleVisibleAsUser(@NonNull String roleName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, "isRoleVisibleAsUser", - getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "isRoleVisibleAsUser", getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -981,8 +1175,9 @@ public class RoleService extends SystemService implements RoleUserState.Callback @Override public boolean isApplicationVisibleForRoleAsUser(@NonNull String roleName, @NonNull String packageName, @UserIdInt int userId) { - UserUtils.enforceCrossUserPermission(userId, false, - "isApplicationVisibleForRoleAsUser", getContext()); + UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, + /* enforceForProfileGroup= */ false, "isApplicationVisibleForRoleAsUser", + getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.e(LOG_TAG, "user " + userId + " does not exist"); return false; @@ -998,6 +1193,70 @@ public class RoleService extends SystemService implements RoleUserState.Callback } @Override + public List<String> getDefaultHoldersForTest(String roleName) { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "getDefaultHoldersForTest not available"); + getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, + "getDefaultHoldersForTest"); + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName); + + synchronized (mLock) { + return mDefaultHoldersForTest.getOrDefault(roleName, Collections.emptyList()); + } + } + + @Override + public void setDefaultHoldersForTest(String roleName, List<String> packageNames) { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "setDefaultHoldersForTest not available"); + getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, + "setDefaultHoldersForTest"); + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName); + + synchronized (mLock) { + if (packageNames == null || packageNames.isEmpty()) { + mDefaultHoldersForTest.remove(roleName); + } else { + mDefaultHoldersForTest.put(roleName, packageNames); + } + } + } + + @Override + public boolean isRoleVisibleForTest(String roleName) { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "isRoleVisibleForTest not available"); + getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, + "isRoleVisibleForTest"); + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName); + + synchronized (mLock) { + return mRolesVisibleForTest.contains(roleName); + } + } + + @Override + public void setRoleVisibleForTest(String roleName, boolean visible) { + Preconditions.checkState(RoleFlags.isProfileGroupExclusivityAvailable(), + "setRoleVisibleForTest not available"); + getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, + "setRoleVisibleForTest"); + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkArgumentIsSupported(TEST_ROLES, roleName); + + synchronized (mLock) { + if (visible) { + mRolesVisibleForTest.add(roleName); + } else { + mRolesVisibleForTest.remove(roleName); + } + } + } + + @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) { if (!checkDumpPermission("role", fout)) { @@ -1039,6 +1298,20 @@ public class RoleService extends SystemService implements RoleUserState.Callback return true; } } + + private void enforceCallingOrSelfAnyPermissions(@NonNull String[] permissions, + @NonNull String message) { + for (String permission : permissions) { + if (getContext().checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED) { + return; + } + } + + throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() + + " nor current process has at least one of" + Arrays.toString(permissions) + + "."); + } } private class Local implements RoleManagerLocal { diff --git a/service/java/com/android/role/RoleShellCommand.java b/service/java/com/android/role/RoleShellCommand.java index 808a64cb4..538e62f47 100644 --- a/service/java/com/android/role/RoleShellCommand.java +++ b/service/java/com/android/role/RoleShellCommand.java @@ -22,11 +22,11 @@ import android.app.role.IRoleManager; import android.os.Build; import android.os.RemoteCallback; import android.os.RemoteException; +import android.permission.internal.compat.UserHandleCompat; import androidx.annotation.RequiresApi; import com.android.modules.utils.BasicShellCommandHandler; -import com.android.permission.compat.UserHandleCompat; import java.io.PrintWriter; import java.util.List; @@ -87,6 +87,10 @@ class RoleShellCommand extends BasicShellCommandHandler { return runClearRoleHolders(); case "set-bypassing-role-qualification": return runSetBypassingRoleQualification(); + case "get-active-user-for-role": + return runGetActiveUserForRole(); + case "set-active-user-for-role": + return runSetActiveUserForRole(); default: return handleDefaultCommands(cmd); } @@ -162,6 +166,27 @@ class RoleShellCommand extends BasicShellCommandHandler { return 0; } + private int runGetActiveUserForRole() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + + int activeUserId = mRoleManager.getActiveUserForRoleAsUser(roleName, userId); + if (activeUserId != UserHandleCompat.USER_NULL) { + getOutPrintWriter().println(activeUserId); + } + return 0; + } + + private int runSetActiveUserForRole() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + int activeUserId = Integer.parseInt(getNextArgRequired()); + int flags = getFlagsMaybe(); + + mRoleManager.setActiveUserForRoleAsUser(roleName, activeUserId, flags, userId); + return 0; + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -174,6 +199,8 @@ class RoleShellCommand extends BasicShellCommandHandler { pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]"); pw.println(" set-bypassing-role-qualification true|false"); + pw.println(" get-active-user-for-role [--user USER_ID] ROLE"); + pw.println(" set-active-user-for-role [--user USER_ID] ROLE ACTIVE_USER_ID [FLAGS]"); pw.println(); } } diff --git a/service/java/com/android/role/RoleUserState.java b/service/java/com/android/role/RoleUserState.java index 39c1e8c3d..c94b58826 100644 --- a/service/java/com/android/role/RoleUserState.java +++ b/service/java/com/android/role/RoleUserState.java @@ -24,6 +24,7 @@ import android.annotation.WorkerThread; import android.os.Build; import android.os.Handler; import android.os.UserHandle; +import android.permission.internal.compat.UserHandleCompat; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -39,6 +40,7 @@ import com.android.role.persistence.RolesState; import com.android.server.role.RoleServicePlatformHelper; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -96,6 +98,10 @@ class RoleUserState { private ArraySet<String> mFallbackEnabledRoles = new ArraySet<>(); @GuardedBy("mLock") + @NonNull + private ArrayMap<String, Integer> mActiveUserIds = new ArrayMap<>(); + + @GuardedBy("mLock") private boolean mWriteScheduled; @GuardedBy("mLock") @@ -242,11 +248,13 @@ class RoleUserState { public void upgradeVersion(@NonNull List<String> legacyFallbackDisabledRoles) { synchronized (mLock) { if (mVersion < VERSION_FALLBACK_STATE_MIGRATED) { + mFallbackEnabledRoles.addAll(mRoles.keySet()); int legacyFallbackDisabledRolesSize = legacyFallbackDisabledRoles.size(); for (int i = 0; i < legacyFallbackDisabledRolesSize; i++) { String roleName = legacyFallbackDisabledRoles.get(i); mFallbackEnabledRoles.remove(roleName); } + Log.v(LOG_TAG, "Migrated fallback enabled roles: " + mFallbackEnabledRoles); mVersion = VERSION_FALLBACK_STATE_MIGRATED; scheduleWriteFileLocked(); } @@ -421,6 +429,42 @@ class RoleUserState { } /** + * Return the active user for the role + * + * @param roleName the name of the role to get the active user for + */ + public int getActiveUserForRole(@NonNull String roleName) { + synchronized (mLock) { + return mActiveUserIds.getOrDefault(roleName, UserHandleCompat.USER_NULL); + } + } + + /** + * Set the active user for the role + * + * @param roleName the name of the role to set the active user for + * @param userId User id to set as active for this role + * @return whether any changes were made + */ + public boolean setActiveUserForRole(@NonNull String roleName, @UserIdInt int userId) { + if (!com.android.permission.flags.Flags.crossUserRoleEnabled()) { + return false; + } + synchronized (mLock) { + Integer currentActiveUserId = mActiveUserIds.get(roleName); + // If we have pre-existing roles that weren't profile group exclusive and don't have an + // active user, ensure we set and write value, and return modified, otherwise other + // users might not have role holder revoked. + if (currentActiveUserId != null && currentActiveUserId == userId) { + return false; + } + mActiveUserIds.put(roleName, userId); + scheduleWriteFileLocked(); + return true; + } + } + + /** * Schedule writing the state to file. */ @GuardedBy("mLock") @@ -447,9 +491,15 @@ class RoleUserState { // Force a reconciliation on next boot if we are bypassing role qualification now. String packagesHash = mBypassingRoleQualification ? null : mPackagesHash; - roles = new RolesState(mVersion, packagesHash, - (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(), - snapshotFallbackEnabledRoles()); + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + roles = new RolesState(mVersion, packagesHash, + (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(), + snapshotFallbackEnabledRoles(), snapshotActiveUserIds()); + } else { + roles = new RolesState(mVersion, packagesHash, + (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(), + snapshotFallbackEnabledRoles()); + } } mPersistence.writeForUser(roles, UserHandle.of(mUserId)); @@ -461,14 +511,17 @@ class RoleUserState { Map<String, Set<String>> roles; Set<String> fallbackEnabledRoles; + Map<String, Integer> activeUserIds; if (roleState != null) { mVersion = roleState.getVersion(); mPackagesHash = roleState.getPackagesHash(); roles = roleState.getRoles(); fallbackEnabledRoles = roleState.getFallbackEnabledRoles(); + activeUserIds = roleState.getActiveUserIds(); } else { roles = mPlatformHelper.getLegacyRoleState(mUserId); fallbackEnabledRoles = roles.keySet(); + activeUserIds = Collections.emptyMap(); } mRoles.clear(); for (Map.Entry<String, Set<String>> entry : roles.entrySet()) { @@ -478,6 +531,10 @@ class RoleUserState { } mFallbackEnabledRoles.clear(); mFallbackEnabledRoles.addAll(fallbackEnabledRoles); + mActiveUserIds.clear(); + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + mActiveUserIds.putAll(activeUserIds); + } if (roleState == null) { scheduleWriteFileLocked(); } @@ -494,12 +551,14 @@ class RoleUserState { int version; String packagesHash; ArrayMap<String, ArraySet<String>> roles; + ArrayMap<String, Integer> activeUserIds; ArraySet<String> fallbackEnabledRoles; synchronized (mLock) { version = mVersion; packagesHash = mPackagesHash; roles = snapshotRolesLocked(); fallbackEnabledRoles = snapshotFallbackEnabledRoles(); + activeUserIds = snapshotActiveUserIds(); } long fieldToken = dumpOutputStream.start(fieldName, fieldId); @@ -512,11 +571,14 @@ class RoleUserState { String roleName = roles.keyAt(rolesIndex); ArraySet<String> roleHolders = roles.valueAt(rolesIndex); boolean fallbackEnabled = fallbackEnabledRoles.contains(roleName); + Integer activeUserId = activeUserIds.get(roleName); long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES); dumpOutputStream.write("name", RoleProto.NAME, roleName); - dumpOutputStream.write("fallback_enabled", RoleProto.FALLBACK_ENABLED, - Boolean.toString(fallbackEnabled)); + dumpOutputStream.write("fallback_enabled", RoleProto.FALLBACK_ENABLED, fallbackEnabled); + if (activeUserId != null) { + dumpOutputStream.write("active_user_id", RoleProto.ACTIVE_USER_ID, activeUserId); + } int roleHoldersSize = roleHolders.size(); for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) { String roleHolder = roleHolders.valueAt(roleHoldersIndex); @@ -562,6 +624,12 @@ class RoleUserState { return new ArraySet<>(mFallbackEnabledRoles); } + @GuardedBy("mLock") + @NonNull + private ArrayMap<String, Integer> snapshotActiveUserIds() { + return new ArrayMap<>(mActiveUserIds); + } + /** * Destroy this user state and delete the corresponding file. Any pending writes to the file * will be cancelled, and any future interaction with this state will throw an exception. diff --git a/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/service/java/com/android/role/persistence/RolesPersistenceImpl.java index 242f7315f..8382d3632 100644 --- a/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ b/service/java/com/android/role/persistence/RolesPersistenceImpl.java @@ -67,6 +67,7 @@ public class RolesPersistenceImpl implements RolesPersistence { private static final String ATTRIBUTE_VERSION = "version"; private static final String ATTRIBUTE_NAME = "name"; private static final String ATTRIBUTE_FALLBACK_ENABLED = "fallbackEnabled"; + private static final String ATTRIBUTE_ACTIVE_USER_ID = "activeUserId"; private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; @VisibleForTesting @@ -144,6 +145,7 @@ public class RolesPersistenceImpl implements RolesPersistence { Map<String, Set<String>> roles = new ArrayMap<>(); Set<String> fallbackEnabledRoles = new ArraySet<>(); + Map<String, Integer> activeUserIds = new ArrayMap<>(); int type; int depth; int innerDepth = parser.getDepth() + 1; @@ -159,12 +161,23 @@ public class RolesPersistenceImpl implements RolesPersistence { if (Boolean.parseBoolean(fallbackEnabled)) { fallbackEnabledRoles.add(roleName); } + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + String activeUserId = parser.getAttributeValue(null, ATTRIBUTE_ACTIVE_USER_ID); + if (activeUserId != null) { + activeUserIds.put(roleName, Integer.parseInt(activeUserId)); + } + } Set<String> roleHolders = parseRoleHolders(parser); roles.put(roleName, roleHolders); } } - return new RolesState(version, packagesHash, roles, fallbackEnabledRoles); + if (com.android.permission.flags.Flags.crossUserRoleEnabled()) { + return new RolesState(version, packagesHash, roles, fallbackEnabledRoles, + activeUserIds); + } else { + return new RolesState(version, packagesHash, roles, fallbackEnabledRoles); + } } @NonNull @@ -190,9 +203,6 @@ public class RolesPersistenceImpl implements RolesPersistence { @Override public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) { - File reserveFile = getReserveCopyFile(user); - reserveFile.delete(); - File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); FileOutputStream outputStream = null; @@ -217,6 +227,8 @@ public class RolesPersistenceImpl implements RolesPersistence { IoUtils.closeQuietly(outputStream); } + File reserveFile = getReserveCopyFile(user); + reserveFile.delete(); try (FileInputStream in = new FileInputStream(file); FileOutputStream out = new FileOutputStream(reserveFile)) { FileUtils.copy(in, out); @@ -245,15 +257,22 @@ public class RolesPersistenceImpl implements RolesPersistence { } Set<String> fallbackEnabledRoles = roles.getFallbackEnabledRoles(); + Map<String, Integer> activeUserIds = roles.getActiveUserIds(); for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) { String roleName = entry.getKey(); Set<String> roleHolders = entry.getValue(); boolean isFallbackEnabled = fallbackEnabledRoles.contains(roleName); + Integer activeUserId = com.android.permission.flags.Flags.crossUserRoleEnabled() + ? activeUserIds.get(roleName) : null; serializer.startTag(null, TAG_ROLE); serializer.attribute(null, ATTRIBUTE_NAME, roleName); serializer.attribute(null, ATTRIBUTE_FALLBACK_ENABLED, Boolean.toString(isFallbackEnabled)); + if (activeUserId != null) { + serializer.attribute( + null, ATTRIBUTE_ACTIVE_USER_ID, Integer.toString(activeUserId)); + } serializeRoleHolders(serializer, roleHolders); serializer.endTag(null, TAG_ROLE); } diff --git a/service/java/com/android/role/persistence/RolesState.java b/service/java/com/android/role/persistence/RolesState.java index a189dd4c2..f1b3d8dfa 100644 --- a/service/java/com/android/role/persistence/RolesState.java +++ b/service/java/com/android/role/persistence/RolesState.java @@ -23,12 +23,13 @@ import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import android.permission.flags.Flags; +import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; /** - * State of all roles. + * State of all roles for a user. * * TODO(b/147914847): Remove @hide when it becomes the default. * @hide @@ -59,6 +60,12 @@ public final class RolesState { private final Set<String> mFallbackEnabledRoles; /** + * The active users for cross user roles. + */ + @NonNull + private final Map<String, Integer> mActiveUserIds; + + /** * Create a new instance of this class. * * @param version the version of the roles @@ -81,10 +88,27 @@ public final class RolesState { @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) public RolesState(int version, @Nullable String packagesHash, @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles) { + this(version, packagesHash, roles, fallbackEnabledRoles, Collections.emptyMap()); + } + + /** + * Create a new instance of this class. + * + * @param version the version of the roles + * @param packagesHash the hash of all packages in the system + * @param roles the roles + * @param fallbackEnabledRoles the roles with fallback enabled + * @param activeUserIds the active users for cross user roles + * @hide + */ + public RolesState(int version, @Nullable String packagesHash, + @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles, + @NonNull Map<String, Integer> activeUserIds) { mVersion = version; mPackagesHash = packagesHash; mRoles = roles; mFallbackEnabledRoles = fallbackEnabledRoles; + mActiveUserIds = activeUserIds; } /** @@ -127,6 +151,17 @@ public final class RolesState { return mFallbackEnabledRoles; } + /** + * Get the active users for cross user roles. + * + * @return active users for cross user roles + * @hide + */ + @NonNull + public Map<String, Integer> getActiveUserIds() { + return mActiveUserIds; + } + @Override public boolean equals(Object object) { if (this == object) { @@ -139,11 +174,12 @@ public final class RolesState { return mVersion == that.mVersion && Objects.equals(mPackagesHash, that.mPackagesHash) && Objects.equals(mRoles, that.mRoles) - && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles); + && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles) + && Objects.equals(mActiveUserIds, that.mActiveUserIds); } @Override public int hashCode() { - return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles); + return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles, mActiveUserIds); } } diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java index 7c7ade3f1..7f4e2e166 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java @@ -17,7 +17,6 @@ package com.android.safetycenter; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; -import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM; import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED; import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY; @@ -388,6 +387,8 @@ public final class SafetyCenterDataFactory { SafetySourcesGroup safetySourcesGroup, String defaultPackageName, UserProfileGroup userProfileGroup) { + HighestSeverityIssueOnlyIssue highestSeverityIssueOnlyIssue = + new HighestSeverityIssueOnlyIssue(); int groupSafetyCenterEntryLevel = SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; List<SafetySource> safetySources = safetySourcesGroup.getSafetySources(); @@ -415,6 +416,7 @@ public final class SafetyCenterDataFactory { groupSafetyCenterEntryLevel, addSafetyCenterEntry( safetyCenterOverallState, + highestSeverityIssueOnlyIssue, entries, safetySource, defaultPackageName, @@ -436,7 +438,10 @@ public final class SafetyCenterDataFactory { CharSequence groupSummary = getSafetyCenterEntryGroupSummary( - safetySourcesGroup, groupSafetyCenterEntryLevel, entries); + safetySourcesGroup, + groupSafetyCenterEntryLevel, + entries, + highestSeverityIssueOnlyIssue); safetyCenterEntryOrGroups.add( new SafetyCenterEntryOrGroup( new SafetyCenterEntryGroup.Builder( @@ -471,7 +476,8 @@ public final class SafetyCenterDataFactory { private CharSequence getSafetyCenterEntryGroupSummary( SafetySourcesGroup safetySourcesGroup, @SafetyCenterEntry.EntrySeverityLevel int groupSafetyCenterEntryLevel, - List<SafetyCenterEntry> entries) { + List<SafetyCenterEntry> entries, + HighestSeverityIssueOnlyIssue highestSeverityIssueOnlyIssue) { switch (groupSafetyCenterEntryLevel) { case SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING: case SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION: @@ -500,6 +506,16 @@ public final class SafetyCenterDataFactory { } } + SafetySourceIssue highestSeverityIssueOnlySafetySourceIssue = + highestSeverityIssueOnlyIssue.mSafetySourceIssue; + if (highestSeverityIssueOnlySafetySourceIssue != null + && toSafetyCenterEntrySeverityLevel( + highestSeverityIssueOnlySafetySourceIssue + .getSeverityLevel()) + == groupSafetyCenterEntryLevel) { + return highestSeverityIssueOnlySafetySourceIssue.getTitle(); + } + return getDefaultGroupSummary(safetySourcesGroup, entries); case SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED: return getDefaultGroupSummary(safetySourcesGroup, entries); @@ -554,6 +570,7 @@ public final class SafetyCenterDataFactory { @SafetyCenterEntry.EntrySeverityLevel private int addSafetyCenterEntry( SafetyCenterOverallState safetyCenterOverallState, + HighestSeverityIssueOnlyIssue highestSeverityIssueOnlyIssue, List<SafetyCenterEntry> entries, SafetySource safetySource, String defaultPackageName, @@ -562,13 +579,10 @@ public final class SafetyCenterDataFactory { boolean isUserRunning) { SafetyCenterEntry safetyCenterEntry = toSafetyCenterEntry( - safetySource, - defaultPackageName, - userId, - profileType, - isUserRunning); + safetySource, defaultPackageName, userId, profileType, isUserRunning); if (safetyCenterEntry == null) { - return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + return getSafetyCenterEntrySeverityLevelFromIssues( + highestSeverityIssueOnlyIssue, safetySource, userId, isUserRunning); } safetyCenterOverallState.addEntryOverallSeverityLevel( @@ -579,6 +593,64 @@ public final class SafetyCenterDataFactory { return safetyCenterEntry.getSeverityLevel(); } + @SafetyCenterEntry.EntrySeverityLevel + private int getSafetyCenterEntrySeverityLevelFromIssues( + HighestSeverityIssueOnlyIssue highestSeverityIssueOnlyIssue, + SafetySource safetySource, + @UserIdInt int userId, + boolean isUserRunning) { + if (!Flags.safetyCenterIssueOnlyAffectsGroupStatus()) { + return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + } + if (safetySource.getType() != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + } + if (!isUserRunning) { + // Issues don't show for non-running users. + return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + } + SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); + SafetySourceData safetySourceData = + mSafetyCenterDataManager.getSafetySourceDataInternal(key); + if (safetySourceData == null) { + return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + } + List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues(); + int safetyCenterEntrySeverityLevel = SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED; + for (int i = 0; i < safetySourceIssues.size(); i++) { + SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); + + SafetyCenterIssueKey safetyCenterIssueKey = + SafetyCenterIssueKey.newBuilder() + .setSafetySourceId(safetySource.getId()) + .setSafetySourceIssueId(safetySourceIssue.getId()) + .setUserId(userId) + .build(); + + int safetySourceIssueSeverityLevel = safetySourceIssue.getSeverityLevel(); + + if (mSafetyCenterDataManager.isIssueDismissed( + safetyCenterIssueKey, safetySourceIssueSeverityLevel)) { + continue; + } + + SafetySourceIssue highestSeverityIssueOnlySafetySourceIssue = + highestSeverityIssueOnlyIssue.mSafetySourceIssue; + if (highestSeverityIssueOnlySafetySourceIssue == null + || safetySourceIssueSeverityLevel + > highestSeverityIssueOnlySafetySourceIssue.getSeverityLevel()) { + highestSeverityIssueOnlyIssue.mSafetySourceIssue = safetySourceIssue; + } + + safetyCenterEntrySeverityLevel = + mergeSafetyCenterEntrySeverityLevels( + safetyCenterEntrySeverityLevel, + toSafetyCenterEntrySeverityLevel(safetySourceIssueSeverityLevel)); + } + + return safetyCenterEntrySeverityLevel; + } + @Nullable private SafetyCenterEntry toSafetyCenterEntry( SafetySource safetySource, @@ -783,11 +855,7 @@ public final class SafetyCenterDataFactory { boolean isUserRunning) { SafetyCenterStaticEntry staticEntry = toSafetyCenterStaticEntry( - safetySource, - defaultPackageName, - userId, - profileType, - isUserRunning); + safetySource, defaultPackageName, userId, profileType, isUserRunning); if (staticEntry == null) { return; } @@ -987,7 +1055,7 @@ public final class SafetyCenterDataFactory { Log.w( TAG, - "Unexpected SafetySourceData.SeverityLevel in SafetySourceStatus: " + "Unexpected SafetySourceData.SeverityLevel in SafetySourceData: " + safetySourceSeverityLevel); return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN; } @@ -1155,7 +1223,7 @@ public final class SafetyCenterDataFactory { return getIcuPluralsString( "overall_severity_level_action_taken_summary", numAutomaticIssues); } - // Fall through. + // Fall through. case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION: case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING: return getIcuPluralsString("overall_severity_n_alerts_summary", numIssues); @@ -1201,7 +1269,7 @@ public final class SafetyCenterDataFactory { if (!overallSeverityUnknown) { return null; } - // Fall through. + // Fall through. case SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS: return mSafetyCenterResourcesApk.getStringByName("scanning_title"); } @@ -1232,9 +1300,9 @@ public final class SafetyCenterDataFactory { return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId()); case PROFILE_TYPE_MANAGED: return DevicePolicyResources.getSafetySourceWorkString( - mSafetyCenterResourcesApk, - safetySource.getId(), - safetySource.getTitleForWorkResId()); + mSafetyCenterResourcesApk, + safetySource.getId(), + safetySource.getTitleForWorkResId()); case PROFILE_TYPE_PRIVATE: if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { return mSafetyCenterResourcesApk.getString( @@ -1337,4 +1405,8 @@ public final class SafetyCenterDataFactory { return Math.max(left, right); } } + + private static final class HighestSeverityIssueOnlyIssue { + @Nullable private SafetySourceIssue mSafetySourceIssue = null; + } } diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java index e51d3a1cf..4e81280f4 100644 --- a/service/java/com/android/safetycenter/SafetyCenterFlags.java +++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java @@ -28,6 +28,7 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; import com.android.safetycenter.resources.SafetyCenterResourcesApk; import java.io.PrintWriter; @@ -123,6 +124,8 @@ public final class SafetyCenterFlags { private static final String RESURFACE_ISSUE_DELAYS_DEFAULT = ""; private static final Duration RESURFACE_ISSUE_DELAYS_DEFAULT_DURATION = Duration.ofDays(180); + private static final ArraySet<String> sAllowedNotificationSources = + new ArraySet<>(new String[] {"GoogleAppProtectionService"}); private static final ArraySet<String> sAllowedNotificationSourcesUPlus = new ArraySet<>(new String[] {"GoogleBackupAndRestore"}); @@ -229,6 +232,9 @@ public final class SafetyCenterFlags { /** Returns whether Safety Center is enabled. */ public static boolean getSafetyCenterEnabled() { + if (Flags.safetyCenterEnabledNoDeviceConfig() && SdkLevel.isAtLeastU()) { + return true; + } return getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, SdkLevel.isAtLeastU()); } @@ -251,15 +257,17 @@ public final class SafetyCenterFlags { */ public static ArraySet<String> getNotificationsAllowedSourceIds() { ArraySet<String> sources = getNotificationsAllowedSourceIdsFlag(); + // This is a hack to update the flag value via mainline update. Reasons why we can't do this + // via: + // remote flag update - these are generally avoided and considered risky + // XML config - it would break GTS tests for OEMs that have a separate config copy + // default flag value - it would also require a remote flag update + if (Flags.odadNotificationsSupported()) { + sources.addAll(sAllowedNotificationSources); + } if (SdkLevel.isAtLeastU()) { - // This is a hack to update the flag value via mainline update. Reasons why we can't do - // this via: - // remote flag update - these are generally avoided and considered risky - // XML config - it would break GTS tests for OEMs that have a separate config copy - // default flag value - it would also require a remote flag update sources.addAll(sAllowedNotificationSourcesUPlus); } - return sources; } diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java index c9a5e1c03..6df4184e1 100644 --- a/service/java/com/android/safetycenter/SafetyCenterService.java +++ b/service/java/com/android/safetycenter/SafetyCenterService.java @@ -244,6 +244,9 @@ public final class SafetyCenterService extends SystemService { @GuardedBy("mApiLock") private void registerSafetyCenterEnabledListenerLocked() { + if (Flags.safetyCenterEnabledNoDeviceConfig() && SdkLevel.isAtLeastU()) { + return; + } SafetyCenterEnabledListener safetyCenterEnabledListener = new SafetyCenterEnabledListener(); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_PRIVACY, @@ -691,7 +694,8 @@ public final class SafetyCenterService extends SystemService { /** Enforces cross user permission and returns whether the user is valid. */ private boolean enforceCrossUserPermission(String message, @UserIdInt int userId) { UserUtils.enforceCrossUserPermission( - userId, /* allowAll= */ false, message, getContext()); + userId, /* allowAll= */ false, /* enforceForProfileGroup= */ false, message, + getContext()); if (!UserUtils.isUserExistent(userId, getContext())) { Log.w( TAG, @@ -1151,9 +1155,7 @@ public final class SafetyCenterService extends SystemService { if (!UserProfileGroup.isSupported(userId, context)) { Log.i( TAG, - "Received broadcast for user id: " - + userId - + ", which is an unsupported user"); + "Received broadcast for user id: " + userId + ", which is an unsupported user"); return false; } if (Intent.ACTION_USER_SWITCHED.equals(action) @@ -1166,11 +1168,7 @@ public final class SafetyCenterService extends SystemService { return false; } if (isProfileAddedOrAvailable(action) && !UserUtils.isUserExistent(userId, context)) { - Log.w( - TAG, - "Received broadcast for user id: " - + userId - + ", which does not exist"); + Log.w(TAG, "Received broadcast for user id: " + userId + ", which does not exist"); return false; } return true; diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java index 46a440bf7..1f5258437 100644 --- a/service/java/com/android/safetycenter/UserProfileGroup.java +++ b/service/java/com/android/safetycenter/UserProfileGroup.java @@ -21,15 +21,12 @@ import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.UserIdInt; import android.content.Context; -import android.content.pm.PackageManager; import android.os.Binder; -import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.permission.internal.compat.UserHandleCompat; import android.util.Log; -import androidx.annotation.Nullable; - import com.android.permission.util.UserUtils; import java.lang.annotation.Retention; @@ -49,8 +46,6 @@ import java.util.Objects; public final class UserProfileGroup { private static final String TAG = "UserProfileGroup"; - // UserHandle#USER_NULL is a @TestApi so it cannot be accessed from the mainline module. - public static final @UserIdInt int USER_NULL = -10000; @UserIdInt private final int mProfileParentUserId; private final int[] mManagedProfilesUserIds; @@ -134,20 +129,16 @@ public final class UserProfileGroup { * is disabled. */ public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) { - UserManager userManager = getUserManagerForUser(userId, context); - List<UserHandle> userProfiles = getEnabledUserProfiles(userManager); - UserHandle profileParent = getProfileParent(userManager, userId); - int profileParentUserId = userId; - if (profileParent != null) { - profileParentUserId = profileParent.getIdentifier(); - } + Context userContext = UserUtils.getUserContext(userId, context); + List<UserHandle> userProfiles = UserUtils.getUserProfiles(userContext); + int profileParentUserId = UserUtils.getProfileParentIdOrSelf(userId, userContext); int[] managedProfilesUserIds = new int[userProfiles.size()]; int[] managedRunningProfilesUserIds = new int[userProfiles.size()]; int managedProfilesUserIdsLen = 0; int managedRunningProfilesUserIdsLen = 0; - int privateProfileUserId = USER_NULL; + int privateProfileUserId = UserHandleCompat.USER_NULL; boolean privateProfileRunning = false; for (int i = 0; i < userProfiles.size(); i++) { @@ -192,23 +183,10 @@ public final class UserProfileGroup { } private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) { - Context userContext = getUserContext(context, UserHandle.of(userId)); + Context userContext = UserUtils.getUserContext(userId, context); return requireNonNull(userContext.getSystemService(UserManager.class)); } - private static Context getUserContext(Context context, UserHandle userHandle) { - if (Process.myUserHandle().equals(userHandle)) { - return context; - } else { - try { - return context.createPackageContextAsUser( - context.getPackageName(), /* flags= */ 0, userHandle); - } catch (PackageManager.NameNotFoundException doesNotHappen) { - throw new IllegalStateException(doesNotHappen); - } - } - } - private static boolean isProfile(@UserIdInt int userId, Context context) { // This call requires the INTERACT_ACROSS_USERS permission. final long callingId = Binder.clearCallingIdentity(); @@ -220,27 +198,6 @@ public final class UserProfileGroup { } } - private static List<UserHandle> getEnabledUserProfiles(UserManager userManager) { - // This call requires the QUERY_USERS permission. - final long callingId = Binder.clearCallingIdentity(); - try { - return userManager.getUserProfiles(); - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - - @Nullable - private static UserHandle getProfileParent(UserManager userManager, @UserIdInt int userId) { - // This call requires the INTERACT_ACROSS_USERS permission. - final long callingId = Binder.clearCallingIdentity(); - try { - return userManager.getProfileParent(UserHandle.of(userId)); - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - /** Returns the profile parent user id of the {@link UserProfileGroup}. */ public int getProfileParentUserId() { return mProfileParentUserId; @@ -262,7 +219,7 @@ public final class UserProfileGroup { /* destPos= */ 1, mManagedProfilesUserIds.length); - if (mPrivateProfileUserId != USER_NULL) { + if (mPrivateProfileUserId != UserHandleCompat.USER_NULL) { allProfileIds[allProfileIds.length - 1] = mPrivateProfileUserId; } @@ -303,7 +260,7 @@ public final class UserProfileGroup { case PROFILE_TYPE_MANAGED: return mManagedProfilesUserIds; case PROFILE_TYPE_PRIVATE: - return mPrivateProfileUserId != USER_NULL + return mPrivateProfileUserId != UserHandleCompat.USER_NULL ? new int[]{mPrivateProfileUserId} : new int[]{}; default: Log.w(TAG, "profiles requested for unexpected profile type " + profileType); @@ -342,7 +299,7 @@ public final class UserProfileGroup { private int getNumProfiles() { return 1 + mManagedProfilesUserIds.length - + (mPrivateProfileUserId == USER_NULL ? 0 : 1); + + (mPrivateProfileUserId == UserHandleCompat.USER_NULL ? 0 : 1); } /** @@ -395,7 +352,8 @@ public final class UserProfileGroup { } } - return USER_NULL != mPrivateProfileUserId && userId == mPrivateProfileUserId; + return UserHandleCompat.USER_NULL != mPrivateProfileUserId + && userId == mPrivateProfileUserId; } @Override diff --git a/service/lint-baseline.xml b/service/lint-baseline.xml index b10928320..6aaf3da8c 100644 --- a/service/lint-baseline.xml +++ b/service/lint-baseline.xml @@ -35,17 +35,6 @@ </issue> <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.os.UserHandle#getUid`" - errorLine1=" return UserHandle.of(userId).getUid(appId);" - errorLine2=" ~~~~~~"> - <location - file="packages/modules/Permission/service/java/com/android/permission/compat/UserHandleCompat.java" - line="57" - column="38"/> - </issue> - - <issue id="FlaggedApi" message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `writeFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)" errorLine1=" roles = new RolesState(mVersion, packagesHash," diff --git a/service/proguard.flags b/service/proguard.flags index a504239a1..979a7febb 100644 --- a/service/proguard.flags +++ b/service/proguard.flags @@ -1,4 +1,9 @@ # Keep classes that implements RoleBehavior, which are used by reflection. -keep class * implements com.android.role.controller.model.RoleBehavior { *; -}
\ No newline at end of file +} + +# Keep Flags classes because they may be referenced by reflection in roles.xml +-keep class **.Flags { + *; +} diff --git a/service/proto/role_service.proto b/service/proto/role_service.proto index f982ead5b..fb1866d83 100644 --- a/service/proto/role_service.proto +++ b/service/proto/role_service.proto @@ -56,4 +56,7 @@ message RoleProto { // Whether fallback holders are enabled for this role. optional bool fallback_enabled = 3; + + // The active user id of this cross user role. + optional int32 active_user_id = 4; } diff --git a/tests/apex/Android.bp b/tests/apex/Android.bp index 83bf4e252..9dfbdf589 100644 --- a/tests/apex/Android.bp +++ b/tests/apex/Android.bp @@ -27,9 +27,12 @@ android_test { ], static_libs: [ "service-permission.impl", + "services.core", "androidx.test.rules", "androidx.test.ext.junit", "androidx.test.ext.truth", + "com.android.permission.flags-aconfig-java", + "flag-junit", "mockito-target-extended-minus-junit4", ], jni_libs: [ diff --git a/tests/apex/AndroidTest.xml b/tests/apex/AndroidTest.xml index b1af0f53e..c3807eb9d 100644 --- a/tests/apex/AndroidTest.xml +++ b/tests/apex/AndroidTest.xml @@ -20,6 +20,7 @@ <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="apct-instrumentation" /> <option name="test-tag" value="PermissionApexTests" /> + <option name="hidden-api-checks" value="false" /> <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" /> <!-- Install test --> diff --git a/tests/apex/java/com/android/role/RoleUserStateTest.kt b/tests/apex/java/com/android/role/RoleUserStateTest.kt new file mode 100644 index 000000000..8b0c51174 --- /dev/null +++ b/tests/apex/java/com/android/role/RoleUserStateTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.role + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.server.role.RoleServicePlatformHelper +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock + +@RunWith(AndroidJUnit4::class) +class RoleUserStateTest { + private var roleServicePlatformHelper = mock(RoleServicePlatformHelper::class.java) + private var roleUserStateCallback = mock(RoleUserState.Callback::class.java) + + private val userId = UserHandle.myUserId() + private val roleUserState = + RoleUserState(userId, roleServicePlatformHelper, roleUserStateCallback, false) + + @Before + fun setUp() { + setUpRole(ROLE_NAME_1, true) + setUpRole(ROLE_NAME_2, false) + } + + private fun setUpRole(roleName: String, fallbackEnabled: Boolean) { + roleUserState.addRoleName(roleName) + roleUserState.setFallbackEnabled(roleName, fallbackEnabled) + } + + @Test + fun testUpgradeVersion_upgradeNotNeeded_remainsUnchanged() { + roleUserState.version = RoleUserState.VERSION_FALLBACK_STATE_MIGRATED + val legacyFallbackDisabledRoles = listOf(ROLE_NAME_1, ROLE_NAME_2) + + roleUserState.upgradeVersion(legacyFallbackDisabledRoles) + + assertRoleFallbackState(ROLE_NAME_1, roleUserState.isFallbackEnabled(ROLE_NAME_1), true) + assertRoleFallbackState(ROLE_NAME_2, roleUserState.isFallbackEnabled(ROLE_NAME_2), false) + } + + @Test + fun testUpgradeVersion_upgradeNeeded_disabledFallbackStateMigrated() { + roleUserState.version = RoleUserState.VERSION_UNDEFINED + val legacyFallbackDisabledRoles = listOf(ROLE_NAME_1, ROLE_NAME_2) + + roleUserState.upgradeVersion(legacyFallbackDisabledRoles) + + assertRoleFallbackState(ROLE_NAME_1, roleUserState.isFallbackEnabled(ROLE_NAME_1), false) + assertRoleFallbackState(ROLE_NAME_2, roleUserState.isFallbackEnabled(ROLE_NAME_2), false) + } + + @Test + fun testUpgradeVersion_upgradeNeeded_enabledFallbackStateMigrated() { + roleUserState.version = RoleUserState.VERSION_UNDEFINED + val legacyFallbackDisabledRoles = emptyList<String>() + + roleUserState.upgradeVersion(legacyFallbackDisabledRoles) + + assertRoleFallbackState(ROLE_NAME_1, roleUserState.isFallbackEnabled(ROLE_NAME_1), true) + assertRoleFallbackState(ROLE_NAME_2, roleUserState.isFallbackEnabled(ROLE_NAME_2), true) + } + + private fun assertRoleFallbackState(roleName: String, actual: Boolean, expected: Boolean) { + assertWithMessage( + "Fallback enabled state for role: $roleName is $actual while" + + " is expected to be $expected" + ) + .that(actual) + .isEqualTo(expected) + } + + companion object { + private const val ROLE_NAME_1 = "ROLE_NAME_1" + private const val ROLE_NAME_2 = "ROLE_NAME_2" + } +} diff --git a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt index 6500b3926..e9c93a33a 100644 --- a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt +++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt @@ -20,12 +20,18 @@ import android.content.ApexEnvironment import android.content.Context import android.os.Process import android.os.UserHandle +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.platform.app.InstrumentationRegistry import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.permission.flags.Flags import com.google.common.truth.Truth.assertThat import java.io.File import org.junit.After +import org.junit.Assume.assumeFalse import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -49,11 +55,22 @@ class RolesPersistenceTest { private val persistence = RolesPersistenceImpl {} private val defaultRoles = mapOf(ROLE_NAME to setOf(HOLDER_1, HOLDER_2)) + private val activeUserIds = mapOf(ROLE_NAME to USER_ID) private val stateVersionUndefined = RolesState(VERSION_UNDEFINED, PACKAGE_HASH, defaultRoles) private val stateVersionFallbackMigrated = RolesState(VERSION_FALLBACK_MIGRATED, PACKAGE_HASH, defaultRoles, setOf(ROLE_NAME)) + private val stateVersionActiveUserIds = + RolesState( + VERSION_ACTIVE_USER_IDS, + PACKAGE_HASH, + defaultRoles, + setOf(ROLE_NAME), + activeUserIds, + ) private val user = Process.myUserHandle() + @get:Rule val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before fun setUp() { createMockDataDirectory() @@ -84,16 +101,41 @@ class RolesPersistenceTest { mockitoSession.finishMocking() } + @RequiresFlagsDisabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) @Test fun testWriteRead() { + assumeFalse(stateVersion == StateVersion.VERSION_ACTIVE_USER_IDS) persistence.writeForUser(state, user) val persistedState = persistence.readForUser(user) assertThat(persistedState).isEqualTo(state) } + @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @Test + fun testWriteRead_supportsActiveUser() { + persistence.writeForUser(state, user) + val persistedState = persistence.readForUser(user) + + assertThat(persistedState).isEqualTo(state) + } + + @RequiresFlagsDisabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) @Test fun testWriteCorruptReadFromReserveCopy() { + assumeFalse(stateVersion == StateVersion.VERSION_ACTIVE_USER_IDS) + persistence.writeForUser(state, user) + // Corrupt the primary file. + RolesPersistenceImpl.getFile(user) + .writeText("<roles version=\"-1\"><role name=\"com.foo.bar\"><holder") + val persistedState = persistence.readForUser(user) + + assertThat(persistedState).isEqualTo(state) + } + + @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @Test + fun testWriteCorruptReadFromReserveCopy_supportsActiveUser() { persistence.writeForUser(state, user) // Corrupt the primary file. RolesPersistenceImpl.getFile(user) @@ -116,11 +158,13 @@ class RolesPersistenceTest { when (stateVersion) { StateVersion.VERSION_UNDEFINED -> stateVersionUndefined StateVersion.VERSION_FALLBACK_MIGRATED -> stateVersionFallbackMigrated + StateVersion.VERSION_ACTIVE_USER_IDS -> stateVersionActiveUserIds } enum class StateVersion { VERSION_UNDEFINED, - VERSION_FALLBACK_MIGRATED + VERSION_FALLBACK_MIGRATED, + VERSION_ACTIVE_USER_IDS, } companion object { @@ -130,10 +174,12 @@ class RolesPersistenceTest { private const val VERSION_UNDEFINED = -1 private const val VERSION_FALLBACK_MIGRATED = 1 + private const val VERSION_ACTIVE_USER_IDS = 2 private const val APEX_MODULE_NAME = "com.android.permission" private const val PACKAGE_HASH = "packagesHash" private const val ROLE_NAME = "roleName" private const val HOLDER_1 = "holder1" private const val HOLDER_2 = "holder2" + private const val USER_ID = 10 } } diff --git a/tests/cts/permission/Android.bp b/tests/cts/permission/Android.bp index 43a7be006..e4fbb81b6 100644 --- a/tests/cts/permission/Android.bp +++ b/tests/cts/permission/Android.bp @@ -67,8 +67,8 @@ android_test { ], sdk_version: "test_current", libs: [ - "android.test.runner", - "android.test.base", + "android.test.runner.stubs.test", + "android.test.base.stubs.test", ], data: [ ":AppThatDefinesUndefinedPermissionGroupElement", @@ -102,6 +102,7 @@ android_test { ":CtsAppThatRequestsOneTimePermission", ":CtsAppThatRequestsPermissionAandB", ":CtsAppThatRequestsPermissionAandC", + ":CtsAppThatRequestsStoragePermission22", ":CtsAppThatRequestsStoragePermission28", ":CtsAppThatRequestsStoragePermission29", ":CtsAppThatRunsRationaleTests", @@ -127,3 +128,41 @@ android_test { ], per_testcase_directory: true, } + +test_module_config { + name: "CtsPermissionTestCases_Platform", + base: "CtsPermissionTestCases", + test_suites: ["general-tests"], + include_filters: [ + "android.permission.cts.BackgroundPermissionsTest", + "android.permission.cts.PermissionControllerTest", + "android.permission.cts.PermissionFlagsTest", + "android.permission.cts.RuntimePermissionPresentationInfoTest", + "android.permission.cts.SharedUidPermissionsTest", + "android.permission.cts.SplitPermissionTest", + ], +} + +test_module_config { + name: "CtsPermissionTestCases_PermissionController", + base: "CtsPermissionTestCases", + test_suites: ["general-tests"], + include_filters: [ + "android.permission.cts.BackgroundPermissionsTest", + "android.permission.cts.LocationAccessCheckTest", + "android.permission.cts.NotificationListenerCheckTest", + "android.permission.cts.OneTimePermissionTest", + "android.permission.cts.PermissionControllerTest", + "android.permission.cts.PlatformPermissionGroupMappingTest", + ], +} + +test_module_config { + name: "CtsPermissionTestCases_PrivacySources", + base: "CtsPermissionTestCases", + test_suites: ["general-tests"], + include_filters: [ + "android.permission.cts.AccessibilityPrivacySourceTest", + "android.permission.cts.NotificationListenerCheckTest", + ], +} diff --git a/tests/cts/permission/AndroidTest.xml b/tests/cts/permission/AndroidTest.xml index ba0185772..eae2f9ed2 100644 --- a/tests/cts/permission/AndroidTest.xml +++ b/tests/cts/permission/AndroidTest.xml @@ -75,6 +75,7 @@ <option name="push" value="CtsAppThatRequestsLocationPermission29v4.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission29v4.apk" /> <option name="push" value="CtsAppThatRequestsLocationPermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission28.apk" /> <option name="push" value="CtsAppThatRequestsLocationPermission22.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission22.apk" /> + <option name="push" value="CtsAppThatRequestsStoragePermission22.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission22.apk" /> <option name="push" value="CtsAppThatRequestsStoragePermission29.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission29.apk" /> <option name="push" value="CtsAppThatRequestsStoragePermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission28.apk" /> <option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationAndBackgroundPermission28.apk" /> diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/Android.bp b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/Android.bp new file mode 100644 index 000000000..c1d88e68e --- /dev/null +++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/Android.bp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CtsAppThatRequestsLocationAndBackgroundPermission31", + defaults: ["cts_defaults"], + sdk_version: "current", + min_sdk_version: "31", + target_sdk_version: "31", + // Tag this module as a cts test artifact + test_suites: [ + "cts", + "general-tests", + "mts-permission", + "mcts-permission", + ], +} diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/AndroidManifest.xml new file mode 100644 index 000000000..c4cff9b0b --- /dev/null +++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission31/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.permission.cts.appthatrequestpermission" + android:versionCode="3"> + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + + <application android:label="PermissionRequestApp" /> +</manifest> + diff --git a/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp b/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp new file mode 100644 index 000000000..d0b5e68a8 --- /dev/null +++ b/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CtsAppThatRequestsStoragePermission22", + defaults: ["cts_defaults"], + sdk_version: "current", + // Tag this module as a cts test artifact + test_suites: [ + "cts", + "general-tests", + "mts-permission", + ], +} diff --git a/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml b/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml new file mode 100644 index 000000000..5c92a7ea6 --- /dev/null +++ b/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.permission.cts.appthatrequestpermission" + android:versionCode="1"> + + <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" /> + + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + + <application /> +</manifest> + diff --git a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java index f3f47631c..f4bed4ada 100644 --- a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java +++ b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java @@ -24,6 +24,7 @@ import static android.app.AppOpsManager.MODE_FOREGROUND; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.content.pm.PermissionInfo.PROTECTION_INTERNAL; +import static android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP; import static android.permission.cts.PermissionUtils.getAppOp; import static android.permission.cts.PermissionUtils.grantPermission; import static android.permission.cts.PermissionUtils.install; @@ -31,6 +32,7 @@ import static android.permission.cts.PermissionUtils.uninstallApp; import static com.android.compatibility.common.util.SystemUtil.eventually; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertNotEquals; @@ -43,14 +45,21 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; +import android.os.Build; +import android.permission.flags.Flags; import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArrayMap; import android.util.Log; +import androidx.test.filters.SdkSuppress; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.After; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -72,6 +81,9 @@ public class BackgroundPermissionsTest { private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @After public void uninstallTestApp() { uninstallApp(APP_PKG); @@ -79,9 +91,24 @@ public class BackgroundPermissionsTest { @Test @AppModeFull(reason = "Instant apps cannot read properties of other packages") - public void verifybackgroundPermissionsProperties() throws Exception { + public void verifyBackgroundPropertiesForPlatformPermissions() throws Exception { + verifyBackgroundPermissionsProperties("android"); + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @RequiresFlagsEnabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED}) + @Test + @AppModeFull(reason = "Instant apps cannot read properties of other packages") + public void verifyBackgroundPropertiesForHealthPermissions() throws Exception { + String healthPackageName = sContext.getPackageManager().getPermissionGroupInfo( + HEALTH_PERMISSION_GROUP, /* flags= */ 0).packageName; + verifyBackgroundPermissionsProperties(healthPackageName); + } + + private void verifyBackgroundPermissionsProperties(String packageName) + throws Exception { PackageInfo pkg = sContext.getPackageManager().getPackageInfo( - "android", PackageManager.GET_PERMISSIONS); + packageName, PackageManager.GET_PERMISSIONS); ArrayMap<String, String> potentialBackgroundPermissionsToGroup = new ArrayMap<>(); int numPermissions = pkg.permissions.length; @@ -97,11 +124,13 @@ public class BackgroundPermissionsTest { } } + int backgroundPermissionCount = 0; for (int i = 0; i < numPermissions; i++) { PermissionInfo permission = pkg.permissions[i]; String backgroundPermissionName = permission.backgroundPermission; if (backgroundPermissionName != null) { + backgroundPermissionCount += 1; Log.i(LOG_TAG, permission.name + "->" + backgroundPermissionName); // foreground permissions must be dangerous @@ -115,6 +144,8 @@ public class BackgroundPermissionsTest { .containsKey(backgroundPermissionName)); } } + // Tested packages must have at least one permission linked with a background permission. + assertThat(backgroundPermissionCount).isGreaterThan(0); } /** diff --git a/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java index 94557464f..41b0bf97e 100644 --- a/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java +++ b/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java @@ -287,42 +287,6 @@ public class FileSystemPermissionTest { assertFileOwnedByGroup(f, "nfc"); } - @MediumTest - @Test - public void testDevQtaguidSane() throws Exception { - File f = new File("/dev/xt_qtaguid"); - assertFalse(f.canRead()); - assertFalse(f.canWrite()); - assertFalse(f.canExecute()); - - assertFileOwnedBy(f, "root"); - assertFileOwnedByGroup(f, "root"); - } - - @MediumTest - @Test - public void testProcQtaguidCtrlSane() throws Exception { - File f = new File("/proc/net/xt_qtaguid/ctrl"); - assertFalse(f.canRead()); - assertFalse(f.canWrite()); - assertFalse(f.canExecute()); - - assertFileOwnedBy(f, "root"); - assertFileOwnedByGroup(f, "net_bw_acct"); - } - - @MediumTest - @Test - public void testProcQtaguidStatsSane() throws Exception { - File f = new File("/proc/net/xt_qtaguid/stats"); - assertFalse(f.canRead()); - assertFalse(f.canWrite()); - assertFalse(f.canExecute()); - - assertFileOwnedBy(f, "root"); - assertFileOwnedByGroup(f, "net_bw_stats"); - } - private static List<String> procNetFiles = Arrays.asList("anycast6", "arp", "arp_tables_matches", "arp_tables_names", "arp_tables_targets", "dev", "dev_mcast", "fib_trie", "fib_triestat", "hci", "icmp", "icmp6", "if_inet6", "igmp", "igmp6", "ip6_flowlabel", diff --git a/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java index a0637827c..9fff22747 100644 --- a/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java +++ b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java @@ -30,9 +30,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.compatibility.common.util.UserHelper; - -import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -217,11 +214,6 @@ public class NoWifiStatePermissionTest { */ @Test(expected = SecurityException.class) public void testSetWifiEnabled() { - // Skip the test for passenger on Multi-user-multi-display devices for Automotive - UserHelper userHelper = new UserHelper(sContext); - Assume.assumeFalse( - "Skipped for visible background User as wifi is disabled for visible background " - + "user.", userHelper.isVisibleBackgroundUser()); mWifiManager.setWifiEnabled(true); } } diff --git a/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java index f5f222f80..291633aab 100644 --- a/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java +++ b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java @@ -290,35 +290,18 @@ public class OneTimePermissionTest { } private void exitApp() { - boolean[] hasExited = {false}; - try { - new Thread(() -> { - while (!hasExited[0]) { - DreamManager mDreamManager = mContext.getSystemService(DreamManager.class); - mUiDevice.pressHome(); - mUiDevice.pressBack(); - runWithShellPermissionIdentity(() -> { - if (mDreamManager.isDreaming()) { - mDreamManager.stopDream(); - } - }); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } + eventually(() -> { + mUiDevice.pressBack(); + runWithShellPermissionIdentity(() -> { + DreamManager mDreamManager = mContext.getSystemService(DreamManager.class); + if (mDreamManager.isDreaming()) { + mDreamManager.stopDream(); } - }).start(); - eventually(() -> { - runWithShellPermissionIdentity(() -> { - if (mActivityManager.getPackageImportance(APP_PKG_NAME) - <= IMPORTANCE_FOREGROUND) { - throw new AssertionError("Unable to exit application"); - } - }); + Assert.assertFalse("Unable to exit application", + mActivityManager.getPackageImportance(APP_PKG_NAME) + <= IMPORTANCE_FOREGROUND); }); - } finally { - hasExited[0] = true; - } + }); } private void clickOneTimeButton() throws Throwable { diff --git a/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java b/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java index d03e215ed..eb0212cef 100644 --- a/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java +++ b/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java @@ -18,6 +18,7 @@ package android.permission.cts; import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_MEDIA_LOCATION; import static android.Manifest.permission.READ_CALL_LOG; import static android.Manifest.permission.READ_CONTACTS; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; @@ -73,6 +74,8 @@ public class PermissionFlagsTest { TMP_DIR + "CtsAppThatRequestsLocationPermission22.apk"; private static final String APK_LOCATION_28 = TMP_DIR + "CtsAppThatRequestsLocationPermission28.apk"; + private static final String APK_STORAGE_22 = + TMP_DIR + "CtsAppThatRequestsStoragePermission22.apk"; private static final String APK_SYSTEM_ALERT_WINDOW_23 = TMP_DIR + "CtsAppThatRequestsSystemAlertWindow23.apk"; @@ -92,6 +95,16 @@ public class PermissionFlagsTest { } @Test + public void implicitPermissionPreM() throws Exception { + install(APK_STORAGE_22); + + // Test ACCESS_MEDIA_LOCATION which is split from READ_EXTERNAL_STORAGE but won't get + // REVOKE_ON_UPGRADE, while it should still get REVIEW_REQUIRED when pre-M. + assertEquals(FLAG_PERMISSION_REVIEW_REQUIRED, getPermissionFlags(APP_PKG, + ACCESS_MEDIA_LOCATION) & FLAG_PERMISSION_REVIEW_REQUIRED); + } + + @Test public void regularPermission() { install(APK_LOCATION_28); diff --git a/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java index 776a1065e..f2d59cbe7 100755 --- a/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java +++ b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java @@ -45,9 +45,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; import android.content.Context; +import android.health.connect.HealthPermissions; import android.os.Build; import android.permission.PermissionManager; import android.permission.PermissionManager.SplitPermissionInfo; +import android.permission.flags.Flags; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SdkSuppress; @@ -56,6 +62,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.ApiLevelUtil; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,6 +78,9 @@ public class SplitPermissionsSystemTest { private List<SplitPermissionInfo> mSplitPermissions; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Before public void before() { Context context = InstrumentationRegistry.getContext(); @@ -87,9 +97,14 @@ public class SplitPermissionsSystemTest { for (SplitPermissionInfo split : mSplitPermissions) { String splitPermission = split.getSplitPermission(); - boolean isAndroid = splitPermission.startsWith("android"); - if (!isAndroid) { + // Due to limitation with accessing flag values in tests, BODY_SENSORS relevant splits + // are handled in its dedicated tests. + boolean shouldSkip = + !splitPermission.startsWith("android") + || splitPermission.equals(BODY_SENSORS) + || splitPermission.equals(BODY_SENSORS_BACKGROUND); + if (shouldSkip) { continue; } @@ -149,9 +164,6 @@ public class SplitPermissionsSystemTest { case BLUETOOTH_SCAN: assertSplit(split, Build.VERSION_CODES.S, BLUETOOTH, BLUETOOTH_ADMIN); break; - case BODY_SENSORS: - assertSplit(split, Build.VERSION_CODES.TIRAMISU, BODY_SENSORS_BACKGROUND); - break; case ACCESS_MEDIA_LOCATION: case READ_MEDIA_IMAGES: case READ_MEDIA_VIDEO: @@ -160,7 +172,56 @@ public class SplitPermissionsSystemTest { } } - assertEquals(24, seenSplits.size()); + assertEquals(23, seenSplits.size()); + } + + @RequiresFlagsDisabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED}) + @Test + public void + validateBodySensors_beforeGranularHealthPermissions_isSplitToBodySensorsBackground() { + assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q)); + + mSplitPermissions.stream() + .filter(split -> split.getSplitPermission().equals(BODY_SENSORS)) + .findFirst() + .ifPresent( + split -> + assertSplit( + split, + Build.VERSION_CODES.TIRAMISU, + BODY_SENSORS_BACKGROUND)); + } + + @RequiresFlagsEnabled({Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED}) + @Test + public void validateBodySensors_afterGranularHealthPermissions_isSplitToReadHeartRate() { + // TODO: Change this to Baklava when available. + assumeTrue(ApiLevelUtil.isAtLeast(36)); + + SplitPermissionInfo legacyBodySensorPermissionInfo = null; + SplitPermissionInfo readHeartRatePermissionInfo = null; + SplitPermissionInfo bodySensorBackgroundPermissionInfo = null; + for (SplitPermissionInfo split : mSplitPermissions) { + if (split.getSplitPermission().equals(BODY_SENSORS) + && split.getNewPermissions().contains(BODY_SENSORS_BACKGROUND)) { + legacyBodySensorPermissionInfo = split; + } else if (split.getSplitPermission().equals(BODY_SENSORS) + && split.getNewPermissions().contains(HealthPermissions.READ_HEART_RATE)) { + readHeartRatePermissionInfo = split; + } else if (split.getSplitPermission().equals(BODY_SENSORS_BACKGROUND)) { + bodySensorBackgroundPermissionInfo = split; + } + } + // Assert BODY_SENSORS is split to BODY_SENSORS_BACKGROUND and READ_HEART_RATE. + assertSplit( + legacyBodySensorPermissionInfo, + Build.VERSION_CODES.TIRAMISU, + BODY_SENSORS_BACKGROUND); + assertSplit(readHeartRatePermissionInfo, HealthPermissions.READ_HEART_RATE); + // Assert BODY_SENSORS_BACKGROUND is split to READ_HEALTH_DATA_IN_BACKGROUND. + assertSplit( + bodySensorBackgroundPermissionInfo, + HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND); } private void assertSplit(SplitPermissionInfo split, int targetSdk, String... permission) { diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt index c7f23377a..687234582 100644 --- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt +++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt @@ -21,6 +21,7 @@ import android.app.Instrumentation import android.companion.virtual.VirtualDeviceManager import android.companion.virtual.VirtualDeviceParams import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM +import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT import android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA import android.content.ComponentName import android.content.Intent @@ -39,7 +40,9 @@ import android.permissionmultidevice.cts.UiAutomatorUtils.click import android.permissionmultidevice.cts.UiAutomatorUtils.findTextForView import android.permissionmultidevice.cts.UiAutomatorUtils.waitFindObject import android.platform.test.annotations.AppModeFull +import android.platform.test.annotations.RequiresFlagsDisabled import android.platform.test.annotations.RequiresFlagsEnabled +import android.provider.Settings import android.view.Display import android.virtualdevice.cts.common.VirtualDeviceRule import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -47,7 +50,6 @@ import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import com.android.compatibility.common.util.SystemUtil -import com.google.common.truth.Truth import com.google.common.truth.Truth.assertThat import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -88,7 +90,7 @@ class DeviceAwarePermissionGrantTest { .build() ) - val displayConfig = + val displayConfigBuilder = VirtualDeviceRule.createDefaultVirtualDisplayConfigBuilder( DISPLAY_WIDTH, DISPLAY_HEIGHT @@ -98,10 +100,9 @@ class DeviceAwarePermissionGrantTest { DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED or DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY ) - .build() virtualDisplay = - virtualDeviceRule.createManagedVirtualDisplay(virtualDevice, displayConfig)!! + virtualDeviceRule.createManagedVirtualDisplay(virtualDevice, displayConfigBuilder)!! deviceDisplayName = virtualDeviceManager.getVirtualDevice(virtualDevice.deviceId)!!.displayName.toString() } @@ -147,6 +148,7 @@ class DeviceAwarePermissionGrantTest { Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED ) + @RequiresFlagsDisabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun onRemoteDevice_requestPermissionForHostDevice_shouldShowWarningDialog() { requestPermissionOnDevice(virtualDisplay.display.displayId, DEVICE_ID_DEFAULT) @@ -157,6 +159,32 @@ class DeviceAwarePermissionGrantTest { @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, + Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, + Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES + ) + @Test + fun onRemoteDevice_requestPermissionForHostDevice_shouldGrantPermission() { + // Create a virtual device with default policy, so that camera permission request will + // correspond to default device camera access. + virtualDevice = + virtualDeviceRule.createManagedVirtualDevice( + VirtualDeviceParams.Builder() + .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_DEFAULT) + .build() + ) + testGrantPermissionForDevice( + virtualDisplay.display.displayId, + virtualDevice.deviceId, + true, + Settings.Global.getString(defaultDeviceContext.contentResolver, + Settings.Global.DEVICE_NAME), + expectPermissionGrantedOnDefaultDevice = true, + expectPermissionGrantedOnRemoteDevice = false + ) + } + + @RequiresFlagsEnabled( + Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED ) @Test @@ -200,7 +228,7 @@ class DeviceAwarePermissionGrantTest { TestConstants.PERMISSION_RESULT_KEY_PERMISSIONS ) ) - .isEqualTo(arrayOf(DEVICE_AWARE_PERMISSION)) + .isEqualTo(arrayOf(PERMISSION)) assertThat( grantPermissionResult.getIntArray(TestConstants.PERMISSION_RESULT_KEY_GRANT_RESULTS) ) @@ -238,7 +266,7 @@ class DeviceAwarePermissionGrantTest { private fun assertPermissionMessageContainsDeviceName(displayId: Int, deviceName: String) { waitFindObject(By.displayId(displayId).res(PERMISSION_MESSAGE_ID)) val text = findTextForView(By.displayId(displayId).res(PERMISSION_MESSAGE_ID)) - Truth.assertThat(text).contains(deviceName) + assertThat(text).contains(deviceName) } private fun assertAppHasPermissionForDevice(deviceId: Int, expectPermissionGranted: Boolean) { @@ -246,7 +274,7 @@ class DeviceAwarePermissionGrantTest { defaultDeviceContext .createDeviceContext(deviceId) .packageManager - .checkPermission(DEVICE_AWARE_PERMISSION, APP_PACKAGE_NAME) + .checkPermission(PERMISSION, APP_PACKAGE_NAME) if (expectPermissionGranted) { Assert.assertEquals(PackageManager.PERMISSION_GRANTED, checkPermissionResult) @@ -270,7 +298,7 @@ class DeviceAwarePermissionGrantTest { "com.android.permissioncontroller:id/permission_allow_foreground_only_button" const val DEVICE_ID_DEFAULT = 0 const val PERSISTENT_DEVICE_ID_DEFAULT = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT - const val DEVICE_AWARE_PERMISSION = Manifest.permission.CAMERA + const val PERMISSION = Manifest.permission.CAMERA const val TIMEOUT = 5000L private const val DISPLAY_HEIGHT = 1920 private const val DISPLAY_WIDTH = 1080 diff --git a/tests/cts/permissionmultiuser/Android.bp b/tests/cts/permissionmultiuser/Android.bp index b86b02205..23aafb7e0 100644 --- a/tests/cts/permissionmultiuser/Android.bp +++ b/tests/cts/permissionmultiuser/Android.bp @@ -33,6 +33,7 @@ android_test { "compatibility-device-util-axt", "ctstestrunner-axt", "Harrier", + "bedstead-multiuser", "modules-utils-build_system", "Nene", ], diff --git a/tests/cts/permissionmultiuser/AndroidTest.xml b/tests/cts/permissionmultiuser/AndroidTest.xml index 10fd4e7a5..f6834036b 100644 --- a/tests/cts/permissionmultiuser/AndroidTest.xml +++ b/tests/cts/permissionmultiuser/AndroidTest.xml @@ -63,8 +63,8 @@ <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.permissionmultiuser.cts" /> - <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" /> - <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" /> + <option name="exclude-annotation" value="com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile" /> + <option name="exclude-annotation" value="com.android.bedstead.multiuser.annotations.RequireRunOnSecondaryUser" /> <option name="runtime-hint" value="5m" /> </test> diff --git a/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt index 2169f0f72..f3309bd3c 100644 --- a/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt +++ b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt @@ -50,15 +50,16 @@ import android.support.test.uiautomator.UiObject2 import android.util.Log import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry +import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile import com.android.bedstead.harrier.BedsteadJUnit4 import com.android.bedstead.harrier.DeviceState import com.android.bedstead.permissions.annotations.EnsureHasPermission import com.android.bedstead.harrier.annotations.EnsureSecureSettingSet import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature import com.android.bedstead.harrier.annotations.RequireNotWatch -import com.android.bedstead.harrier.annotations.RequireRunOnAdditionalUser -import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile import com.android.bedstead.harrier.annotations.RequireSdkVersion +import com.android.bedstead.multiuser.additionalUser +import com.android.bedstead.multiuser.annotations.RequireRunOnAdditionalUser import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS import com.android.compatibility.common.util.ApiTest import com.android.compatibility.common.util.DeviceConfigStateChangerRule diff --git a/tests/cts/permissionpolicy/Android.bp b/tests/cts/permissionpolicy/Android.bp index b44900758..07fde8bff 100644 --- a/tests/cts/permissionpolicy/Android.bp +++ b/tests/cts/permissionpolicy/Android.bp @@ -26,7 +26,7 @@ android_test { "cts", "general-tests", ], - libs: ["android.test.base"], + libs: ["android.test.base.stubs.test"], static_libs: [ "androidx.test.core", "compatibility-device-util-axt", @@ -37,6 +37,8 @@ android_test { "permission-test-util-lib", "androidx.test.rules", "flag-junit", + "android.app.flags-aconfig", + "android.permission.flags-aconfig-java-export", ], srcs: [ "src/**/*.java", diff --git a/tests/cts/permissionpolicy/AndroidTest.xml b/tests/cts/permissionpolicy/AndroidTest.xml index 662fe3c18..47efacd7c 100644 --- a/tests/cts/permissionpolicy/AndroidTest.xml +++ b/tests/cts/permissionpolicy/AndroidTest.xml @@ -45,7 +45,7 @@ <!-- Create place to store apks --> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="mkdir -p /data/local/tmp/cts-permissionpolicy" /> - <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionpolicy-permissionpolicy"/> + <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionpolicy"/> </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> diff --git a/tests/cts/permissionpolicy/res/raw/OWNERS b/tests/cts/permissionpolicy/res/raw/OWNERS index 6e1a91b88..4f0306f55 100644 --- a/tests/cts/permissionpolicy/res/raw/OWNERS +++ b/tests/cts/permissionpolicy/res/raw/OWNERS @@ -1,6 +1,5 @@ hackbod@google.com patb@google.com -yamasani@google.com michaelwr@google.com narayan@google.com roosa@google.com diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml index af96c9a20..bdcb11865 100644 --- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml @@ -898,13 +898,26 @@ android:featureFlag="android.provider.user_keys" /> <!-- Allows an application to set default account for new contacts. - <p> This permission is only granted to system applications fulfilling the Contacts app role. + <p>This permission is only granted to system applications fulfilling the Contacts app role. <p>Protection level: internal|role @SystemApi @hide --> <permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS" - android:protectionLevel="internal|role" /> + android:protectionLevel="internal|role" + android:featureFlag="!android.provider.new_default_account_api_enabled"/> + + <!-- Allows an application to set default account for new contacts. + <p>This permission is only granted to system applications fulfilling the Contacts app role + and the application with known signers. + <p>Protection level: internal|role|knownSigner + @SystemApi + @hide + --> + <permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS" + android:protectionLevel="internal|role|knownSigner" + android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners" + android:featureFlag="android.provider.new_default_account_api_enabled"/> <!-- ====================================================================== --> <!-- Permissions for accessing user's calendar --> @@ -2070,6 +2083,21 @@ <permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows an application to bind to a + android.service.PopulationDensityProviderService for the purpose of + querying population density. This prevents arbitrary clients connecting + to the service. The system server checks that the provider's intent + service explicitly sets this permission via the android:permission + attribute of the service. + This is only expected to be possessed by the system server outside of + tests. + @FlaggedApi(android.location.flags.Flags.FLAG_POPULATION_DENSITY_PROVIDER) + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_POPULATION_DENSITY_PROVIDER_SERVICE" + android:featureFlag="android.location.flags.population_density_provider" + android:protectionLevel="signature" /> + <!-- ======================================= --> <!-- Permissions for accessing networks --> <!-- ======================================= --> @@ -2396,6 +2424,16 @@ android:label="@string/permlab_nearby_wifi_devices" android:protectionLevel="dangerous" /> + <!-- Required to be able to range to devices using any ranging technology. + @FlaggedApi("android.permission.flags.ranging_permission_enabled") + <p>Protection level: dangerous --> + <permission android:name="android.permission.RANGING" + android:permissionGroup="android.permission-group.UNDEFINED" + android:description="@string/permdesc_ranging" + android:label="@string/permlab_ranging" + android:protectionLevel="dangerous" + android:featureFlag="android.permission.flags.ranging_permission_enabled" /> + <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the user from using them until they are unsuspended. @hide @@ -2564,6 +2602,22 @@ android:label="@string/permlab_getAccounts" /> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> + <!-- @SystemApi Allows access to remove an account. + @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.REMOVE_ACCOUNTS" + android:protectionLevel="signature|role" + android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" /> + + <!-- @SystemApi Allows access to copy an account to another user. + @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.COPY_ACCOUNTS" + android:protectionLevel="signature|role" + android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" /> + <!-- Allows applications to call into AccountAuthenticators. <p>Not for use by third-party applications. --> <permission android:name="android.permission.ACCOUNT_MANAGER" @@ -2606,12 +2660,22 @@ <!-- @SystemApi Allows access to perform vendor effects in the vibrator. <p>Protection level: signature + @FlaggedApi("android.os.vibrator.vendor_vibration_effects") @hide --> <permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS" android:protectionLevel="signature|privileged" android:featureFlag="android.os.vibrator.vendor_vibration_effects" /> + <!-- @SystemApi Allows access to start a vendor vibration session. + <p>Protection level: signature + @FlaggedApi("android.os.vibrator.vendor_vibration_effects") + @hide + --> + <permission android:name="android.permission.START_VIBRATION_SESSIONS" + android:protectionLevel="signature|privileged" + android:featureFlag="android.os.vibrator.vendor_vibration_effects" /> + <!-- @SystemApi Allows access to the vibrator state. <p>Protection level: signature @hide @@ -3857,6 +3921,14 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION" android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to executing app functions. + <p>Protection level: internal|role + @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS" + android:protectionLevel="internal|role" + android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager" /> + <!-- Allows an application to set policy related to subscriptions downloaded by an admin. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. @@ -3915,6 +3987,51 @@ android:protectionLevel="signature|installer" /> <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" /> + <!-- Allows an application to toggle the device's advanced protection mode status. + @FlaggedApi("android.security.aapm_api") + @SystemApi + @hide --> + <permission android:name="android.permission.MANAGE_ADVANCED_PROTECTION_MODE" + android:protectionLevel="signature|privileged" + android:featureFlag="android.security.aapm_api"/> + + <!-- Allows an application to query the device's advanced protection mode status. + @FlaggedApi("android.security.aapm_api") --> + <permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" + android:protectionLevel="normal" + android:featureFlag="android.security.aapm_api"/> + + <!-- Allows an application to read the state of the IntrusionDetectionService + @FlaggedApi(android.security.Flags.FLAG_AFL_API) + @SystemApi + @hide --> + <permission android:name="android.permission.READ_INTRUSION_DETECTION_STATE" + android:featureFlag="android.security.afl_api" + android:protectionLevel="signature|privileged" /> + <uses-permission android:name="android.permission.READ_INTRUSION_DETECTION_STATE" + android:featureFlag="android.security.afl_api"/> + + <!-- Allows an application to change the state of the IntrusionDetectionService + @FlaggedApi(android.security.Flags.FLAG_AFL_API) + @SystemApi + @hide --> + <permission android:name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" + android:featureFlag="android.security.afl_api" + android:protectionLevel="signature|privileged" /> + <uses-permission android:name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" + android:featureFlag="android.security.afl_api"/> + + <!-- Must be required by any IntrusionDetectionEventTransportService to ensure that + only the system can bind to it. + @FlaggedApi(android.security.Flags.FLAG_AFL_API) + @SystemApi + @hide --> + <permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" + android:featureFlag="android.security.afl_api" + android:protectionLevel="signature" /> + <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" + android:featureFlag="android.security.afl_api"/> + <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.--> <permission android:name="android.permission.PROVISION_DEMO_DEVICE" android:protectionLevel="signature|setup|knownSigner" @@ -4058,6 +4175,62 @@ android:protectionLevel="signature" /> <!-- ================================== --> + <!-- Permissions associated with picture profiles and processing --> + <!-- ================================== --> + <eat-comment /> + + <!-- @FlaggedApi(android.media.tv.flags.Flags.apply_picture_profiles) + Allows an app to apply a {@link MediaQualityManager.PictureProfile} to a layer via + {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and, additionally, system apps via + {@link SurfaceControl.Transaction#setPictureProfileHandle}. + --> + <permission android:name="android.permission.APPLY_PICTURE_PROFILE" + android:protectionLevel="normal" + android:featureFlag="android.media.tv.flags.apply_picture_profiles"/> + + <!-- @hide + Allows MediaQualityManager to observe any {@link MediaQualityManager.PictureProfile} + applied to any layer in the system by apps via + {@link MediaCodec.PARAMETER_KEY_PICTURE_PROFILE} and by system apps via + {@link SurfaceControl.Transaction#setPictureProfileHandle}. + --> + <permission android:name="android.permission.OBSERVE_PICTURE_PROFILES" + android:protectionLevel="signature|privileged" + android:featureFlag="android.media.tv.flags.apply_picture_profiles"/> + + <!-- + @SystemApi + @FlaggedApi("android.media.tv.flags.media_quality_fw") + Allows an application to access its picture profile from the media quality database. + <p> Protection level: signature|privileged|vendor privileged + @hide + --> + <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE" + android:protectionLevel="signature|privileged|vendorPrivileged" + android:featureFlag="android.media.tv.flags.media_quality_fw"/> + + <!-- + @SystemApi + @FlaggedApi("android.media.tv.flags.media_quality_fw") + Allows an application to access its sound profile from the media quality database. + <p> Protection level: signature|privileged|vendor privileged + @hide + --> + <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" + android:protectionLevel="signature|privileged|vendorPrivileged" + android:featureFlag="android.media.tv.flags.media_quality_fw"/> + + <!-- + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + Allows an application to read the aggregated color zones on the screen for use cases like + TV ambient backlight usages. + <p> Protection level: normal + --> + <permission android:name="android.permission.READ_COLOR_ZONES" + android:protectionLevel="normal" + android:featureFlag="android.media.tv.flags.media_quality_fw"/> + + <!-- ================================== --> <!-- Permissions affecting the display of other applications --> <!-- ================================== --> <eat-comment /> @@ -4234,6 +4407,18 @@ android:description="@string/permdesc_hideOverlayWindows" android:protectionLevel="normal" /> + <!-- Allows an app to enter Picture-in-Picture mode when the user is not explicitly requesting + it. This includes using {@link PictureInPictureParams.Builder#setAutoEnterEnabled} as well + as lifecycle methods such as {@link Activity#onUserLeaveHint} and {@link Activity#onPause} + to enter PiP when the user leaves the app. + This permission should only be used for certain PiP + <a href="{@docRoot}training/tv/get-started/multitasking#usage-types">usage types</a>. + @FlaggedApi("android.app.enable_tv_implicit_enter_pip_restriction") + --> + <permission android:name="android.permission.TV_IMPLICIT_ENTER_PIP" + android:protectionLevel="normal" + android:featureFlag="android.app.enable_tv_implicit_enter_pip_restriction" /> + <!-- ================================== --> <!-- Permissions affecting the system wallpaper --> <!-- ================================== --> @@ -4740,6 +4925,27 @@ <permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS" android:protectionLevel="signature|privileged|role" /> + <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST) + Allows an application to access the Settings Preference services to read settings exposed + by the system Settings app and system apps that contribute settings surfaced by the + Settings app. + <p>This allows the calling application to read settings values through the host + application, agnostic of underlying storage. --> + <permission android:name="android.permission.READ_SYSTEM_PREFERENCES" + android:protectionLevel="signature|privileged|role" + android:featureFlag="com.android.settingslib.flags.settings_catalyst" /> + + <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_WRITE_SYSTEM_PREFERENCE_PERMISSION_ENABLED) + Allows an application to access the Settings Preference services to write settings + values exposed by the system Settings app and system apps that contribute settings surfaced + in the Settings app. + <p>This allows the calling application to write settings values + through the host application, agnostic of underlying storage. + <p>Protection Level: signature|privileged|appop --> + <permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" + android:protectionLevel="signature|privileged|appop" + android:featureFlag="com.android.settingslib.flags.write_system_preference_permission_enabled" /> + <!-- ========================================= --> <!-- Permissions for special development tools --> <!-- ========================================= --> @@ -5528,6 +5734,17 @@ <permission android:name="android.permission.LOCK_DEVICE" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi + @FlaggedApi(android.security.Flags.FLAG_SECURE_LOCKDOWN) + Allows an application to lock down the device into an enhanced security state. + <p>Not for use by third-party applications. + <p>Protection level: signature|privileged + @hide + --> + <permission android:name="android.permission.MANAGE_SECURE_LOCK_DEVICE" + android:protectionLevel="signature|privileged" + android:featureFlag="android.security.secure_lockdown" /> + <!-- @SystemApi Allows low-level access to setting the orientation (actually rotation) of the screen. <p>Not for use by third-party applications. @@ -6138,6 +6355,15 @@ <permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT" android:protectionLevel="signature|privileged|role" /> + <!-- @SystemApi Allows an application to bypass concurrency restrictions while + recording audio. For example, apps with this permission can continue to record + while a voice call is active.</p> + @FlaggedApi(android.media.audio.Flags.FLAG_CONCURRENT_AUDIO_RECORD_BYPASS_PERMISSION) + @hide --> + <permission android:name="android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION" + android:featureFlag="android.media.audio.concurrent_audio_record_bypass_permission" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to capture audio for hotword detection. <p>Not for use by third-party applications.</p> @hide --> @@ -6440,6 +6666,13 @@ <permission android:name="android.permission.BATTERY_STATS" android:protectionLevel="signature|privileged|development" /> + <!-- @SystemApi @hide Allows an application to collect high-precision PowerMonitor readings + <p>Protection level: signature|privileged|development + @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION) --> + <permission android:name="android.permission.ACCESS_FINE_POWER_MONITORS" + android:protectionLevel="signature|privileged|development" + android:featureFlag="android.permission.flags.fine_power_monitor_permission" /> + <!--Allows an application to manage statscompanion. <p>Not for use by third-party applications. @hide --> @@ -6629,6 +6862,13 @@ <permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows for reading subscription plan fields for status and end date. + @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE) + --> + <permission android:name="android.permission.READ_SUBSCRIPTION_PLANS" + android:protectionLevel="signature|privileged" + android:featureFlag="com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date" /> + <!-- C2DM permission. @hide Used internally. --> @@ -7581,7 +7821,31 @@ <!-- @SystemApi Allows an application to access shared libraries. @hide --> <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" - android:protectionLevel="signature|installer" /> + android:protectionLevel="signature|installer" + android:featureFlag="!android.content.pm.sdk_dependency_installer" /> + + <!-- @SystemApi Allows an application to access shared libraries. + @hide --> + <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" + android:protectionLevel="signature|installer|role" + android:featureFlag="android.content.pm.sdk_dependency_installer" /> + + <!-- @SystemApi Permission held by the system to allow binding to the dependency installer role + holder. + @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + @hide --> + <permission android:name="android.permission.BIND_DEPENDENCY_INSTALLER" + android:protectionLevel="signature" + android:featureFlag="android.content.pm.sdk_dependency_installer" /> + + <!-- @SystemApi Allows an application to install shared libraries of types + {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or + {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE}. + @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + @hide --> + <permission android:name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES" + android:protectionLevel="signature|role" + android:featureFlag="android.content.pm.sdk_dependency_installer" /> <!-- Allows an app to log compat change usage. @hide <p>Not for use by third-party applications.</p> --> @@ -7656,6 +7920,15 @@ <permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" android:protectionLevel="signature|role"/> + <!-- Allows an application to create displays that mirror other displays' content. + <p>Not for use by third-party applications. + <p>Protection level: internal|role + @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE) + @hide @SystemApi --> + <permission android:name="android.permission.ADD_MIRROR_DISPLAY" + android:protectionLevel="internal|role" + android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role" /> + <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. --> <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|role" /> @@ -8072,6 +8345,26 @@ android:protectionLevel="signature|knownSigner" android:knownCerts="@array/config_healthConnectMigrationKnownSigners" /> + <!-- @hide @SystemApi Allows permitted apps to back up Health Connect data and settings. + <p>Protection level: signature|knownSigner + @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") + --> + <permission + android:name="android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS" + android:protectionLevel="signature|knownSigner" + android:knownCerts="@array/config_backupHealthConnectDataAndSettingsKnownSigners" + android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" /> + + <!-- @hide @SystemApi Allows permitted apps to restore Health Connect data and settings. + <p>Protection level: signature|knownSigner + @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") + --> + <permission + android:name="android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS" + android:protectionLevel="signature|knownSigner" + android:knownCerts="@array/config_restoreHealthConnectDataAndSettingsKnownSigners" + android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" /> + <!-- @SystemApi Allows an app to query apps in clone profile. The permission is bidirectional in nature, i.e. cloned apps would be able to query apps in root user. The permission is not meant for 3P apps as of now. @@ -8264,6 +8557,63 @@ <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" android:protectionLevel="signature"/> + <!-- Allows app to enter trade-in-mode. + <p>Protection level: signature|privileged + @hide + --> + <permission android:name="android.permission.ENTER_TRADE_IN_MODE" + android:protectionLevel="signature|privileged" + android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" /> + + <!-- @SystemApi + @FlaggedApi(com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS) + Ability to read program metadata and attach dynamic instrumentation. + <p>Protection level: signature + @hide + --> + <permission android:name="android.permission.DYNAMIC_INSTRUMENTATION" + android:protectionLevel="signature" + android:featureFlag="com.android.art.flags.executable_method_file_offsets" /> + + <!-- @SystemApi + @FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing") + This permission is required when accessing information related to + singleUser-ed TIS session. + <p>This should only be used by OEM. + <p>Protection level: signature|privileged|vendorPrivileged + @hide + --> + <permission android:name="android.permission.SINGLE_USER_TIS_ACCESS" + android:protectionLevel="signature|privileged|vendorPrivileged" + android:featureFlag="android.media.tv.flags.kids_mode_tvdb_sharing"/> + + <!-- @SystemApi + @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") + This permission is required to access the specific text classifier you need from the + TextClassificationManager. + <p>Protection level: signature|role + @hide + --> + <permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE" + android:protectionLevel="signature|role" + android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/> + + <!-- + This permission allows the system to receive PACKAGE_CHANGED broadcasts when the component + state of a non-exported component has been changed. + <p>Not for use by third-party applications. </p> + <p>Protection level: internal + @hide + --> + <permission + android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED" + android:protectionLevel="internal" + android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/> + + <uses-permission + android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED" + android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> @@ -8771,7 +9121,11 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> - <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService" + <service android:name="com.android.server.profcollect.ProfcollectForwardingService$PeriodicTraceJobService" + android:permission="android.permission.BIND_JOB_SERVICE" > + </service> + + <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ReportProcessJobService" android:permission="android.permission.BIND_JOB_SERVICE" > </service> @@ -8850,6 +9204,17 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> + <service android:name="android.app.ecm.EnhancedConfirmationCallTrackerService" + android:permission="android.permission.BIND_INCALL_SERVICE" + android:featureFlag="android.permission.flags.enhanced_confirmation_in_call_apis_enabled" + android:exported="true"> + <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS" + android:value="true" /> + <intent-filter> + <action android:name="android.telecom.InCallService"/> + </intent-filter> + </service> + <service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncInCallService" android:permission="android.permission.BIND_INCALL_SERVICE" android:exported="true"> diff --git a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml index 783cd7f6b..9114a1ecd 100644 --- a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml @@ -88,6 +88,35 @@ android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_mileage" android:description="@string/car_permission_desc_mileage"/> + <permission android:name="android.car.permission.CAR_MILEAGE_3P" + android:permissionGroup="android.permission-group.LOCATION" + android:protectionLevel="dangerous" + android:label="@string/car_permission_label_mileage_3p" + android:description="@string/car_permission_desc_mileage_3p" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> + <permission android:name="android.car.permission.READ_CAR_HORN" + android:permissionGroup="android.car.permission-group.CAR_MONITORING" + android:protectionLevel="dangerous" + android:label="@string/car_permission_label_read_car_horn" + android:description="@string/car_permission_desc_read_car_horn" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> + <permission android:name="android.car.permission.CONTROL_CAR_HORN" + android:protectionLevel="signature|privileged" + android:label="@string/car_permission_label_control_car_horn" + android:description="@string/car_permission_desc_control_car_horn" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> + <permission android:name="android.car.permission.READ_CAR_PEDALS" + android:permissionGroup="android.car.permission-group.CAR_MONITORING" + android:protectionLevel="dangerous" + android:label="@string/car_permission_label_read_car_pedals" + android:description="@string/car_permission_desc_read_car_pedals" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> + <permission android:name="android.car.permission.READ_BRAKE_INFO" + android:permissionGroup="android.car.permission-group.CAR_MONITORING" + android:protectionLevel="dangerous" + android:label="@string/car_permission_label_read_brake_info" + android:description="@string/car_permission_desc_read_brake_info" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> <permission android:name="android.car.permission.CAR_TIRES" android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_car_tires" @@ -186,6 +215,12 @@ android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_control_car_exterior_lights" android:description="@string/car_permission_desc_control_car_exterior_lights"/> + <permission android:name="android.car.permission.READ_CAR_EXTERIOR_LIGHTS" + android:permissionGroup="android.car.permission-group.CAR_MONITORING" + android:protectionLevel="dangerous" + android:label="@string/car_permission_label_read_car_exterior_lights" + android:description="@string/car_permission_desc_car_read_exterior_lights" + android:featureFlag="android.car.feature.android_b_vehicle_properties" /> <permission android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS" android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_car_interior_lights" @@ -620,4 +655,16 @@ android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_bind_app_card_provider" android:description="@string/car_permission_desc_bind_app_card_provider" /> + <permission + android:name="android.car.permission.RECORD_VEHICLE_PROPERTIES" + android:protectionLevel="signature" + android:label="@string/car_permission_label_record_vehicle_properties" + android:description="@string/car_permission_desc_record_vehicle_properties" + android:featureFlag="android.car.feature.car_property_simulation" /> + <permission + android:name="android.car.permission.INJECT_VEHICLE_PROPERTIES" + android:protectionLevel="signature" + android:label="@string/car_permission_label_inject_vehicle_properties" + android:description="@string/car_permission_desc_inject_vehicle_properties" + android:featureFlag="android.car.feature.car_property_simulation" /> </manifest> diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java index 5ce9df514..a58602501 100644 --- a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java +++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java @@ -73,9 +73,6 @@ public class PermissionPolicyTest { private static final String MANAGE_COMPANION_DEVICES_PERMISSION = "android.permission.MANAGE_COMPANION_DEVICES"; - private static final String EXECUTE_APP_FUNCTIONS_PERMISSION = - "android.permission.EXECUTE_APP_FUNCTIONS"; - private static final String LOG_TAG = "PermissionProtectionTest"; private static final String PLATFORM_PACKAGE_NAME = "android"; @@ -533,9 +530,6 @@ public class PermissionPolicyTest { return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE); case MANAGE_COMPANION_DEVICES_PERMISSION: return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE); - case EXECUTE_APP_FUNCTIONS_PERMISSION: - // OEMs are allowed to backport this permission before Android 16. - return true; default: return false; } diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt index 6b3ae5f2e..2ce48af44 100644 --- a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt +++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt @@ -33,6 +33,7 @@ import android.Manifest.permission.NEARBY_WIFI_DEVICES import android.Manifest.permission.PACKAGE_USAGE_STATS import android.Manifest.permission.POST_NOTIFICATIONS import android.Manifest.permission.PROCESS_OUTGOING_CALLS +import android.Manifest.permission.RANGING import android.Manifest.permission.READ_CALENDAR import android.Manifest.permission.READ_CALL_LOG import android.Manifest.permission.READ_CELL_BROADCASTS @@ -58,7 +59,9 @@ import android.app.AppOpsManager.permissionToOp import android.content.pm.PackageManager.GET_PERMISSIONS import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP +import android.health.connect.HealthPermissions import android.os.Build +import android.permission.flags.Flags import android.permission.PermissionManager import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 @@ -187,6 +190,24 @@ class RuntimePermissionProperties { // runtime permission expectedPerms.add(READ_MEDIA_VISUAL_USER_SELECTED) - assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name }) + // Add runtime permissions added in B which were _not_ split from a previously existing + // runtime permission + if (Flags.rangingPermissionEnabled()) { + expectedPerms.add(RANGING) + } + + // Separately check health permissions. + if (Flags.replaceBodySensorPermissionEnabled()) { + assertThat(expectedPerms).contains(HealthPermissions.READ_HEART_RATE); + assertThat(expectedPerms).contains(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND); + + // Remove these from the expected list once we've confirmed their + // present. These are not permissions owned by "android" so won't be + // in the list of platform runtime permissions. + expectedPerms.remove(HealthPermissions.READ_HEART_RATE); + expectedPerms.remove(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND); + } + + assertThat(platformRuntimePerms.map { it.name }).containsExactlyElementsIn(expectedPerms) } } diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt new file mode 100644 index 000000000..e6673718f --- /dev/null +++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permissionpolicy.cts + +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.PermissionInfo +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.compatibility.common.util.CddTest +import com.android.compatibility.common.util.PermissionUtils +import com.android.compatibility.common.util.SystemUtil +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SignaturePermissionAllowlistConfigTest { + private val context = InstrumentationRegistry.getInstrumentation().getTargetContext() + private val packageManager = context.packageManager + + @CddTest(requirements = arrayOf("9.1/C-0-16")) + @Test + fun allPlatformSignedNonSystemPackagesHavePlatformSignaturePermissionsAllowlisted() { + val allowlist = getSignaturePermissionAllowlist() + val platformSignaturePermissionNames = getPlatformSignaturePermissionNames() + val unallowlistedPackageAndPermissions = mutableMapOf<String, MutableList<String>>() + for (packageInfo in getPlatformSignedNonSystemPackageInfos()) { + val permissionNames = allowlist[packageInfo.packageName] + packageInfo.requestedPermissions?.forEach { permissionName -> + if (permissionName in platformSignaturePermissionNames) { + if (permissionNames?.contains(permissionName) != true) { + unallowlistedPackageAndPermissions.getOrPut(packageInfo.packageName) { + mutableListOf() + } += permissionName + } + } + } + } + // TODO(b/365777291): CDD 9.1/C-0-16 is still STRONGLY RECOMMENDED instead of MUST. + if (unallowlistedPackageAndPermissions.isNotEmpty()) { + Log.w( + LOG_TAG, + "Some platform-signed non-system packages don't have their requested platform" + + " signature permissions allowlisted. Suggested signature permission allowlist" + + " additions:\n\n" + + buildSiganturePermissionAllowlist(unallowlistedPackageAndPermissions), + ) + } + } + + private fun getSignaturePermissionAllowlist(): Map<String, Set<String>> { + val allowlist = mutableMapOf<String, MutableSet<String>>() + val partitions = listOf("system", "system-ext", "vendor", "product", "apex") + for (partition in partitions) { + val output = + SystemUtil.runShellCommandOrThrow( + "pm get-signature-permission-allowlist $partition" + ) + .trim() + lateinit var permissionNames: MutableSet<String> + for (line in output.split("\n")) { + val line = line.trim() + when { + line.startsWith("Package: ") -> { + val packageName = line.substring("Package: ".length) + permissionNames = allowlist.getOrPut(packageName) { mutableSetOf() } + } + line.startsWith("Permission: ") -> { + val permissionName = line.substring("Permission: ".length) + permissionNames += permissionName + } + line.isEmpty() -> {} + else -> error("Unknown line in pm get-signature-permission-allowlist: $line") + } + } + } + return allowlist + } + + @CddTest(requirements = arrayOf("9.1/C-0-16")) + @Test + fun allPlatformSignedNonSystemPackagesHavePlatformSignaturePermissionsGranted() { + val platformSignaturePermissionNames = getPlatformSignaturePermissionNames() + val deniedPackageAndPermissions = mutableMapOf<String, MutableList<String>>() + for (packageInfo in getPlatformSignedNonSystemPackageInfos()) { + packageInfo.requestedPermissions?.forEachIndexed { index, permissionName -> + val permissionFlags = packageInfo.requestedPermissionsFlags!![index] + if (permissionName in platformSignaturePermissionNames) { + if (permissionFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED == 0) { + deniedPackageAndPermissions.getOrPut(packageInfo.packageName) { + mutableListOf() + } += permissionName + } + } + } + } + // TODO(b/365777291): CDD 9.1/C-0-16 is still STRONGLY RECOMMENDED instead of MUST. + if (deniedPackageAndPermissions.isNotEmpty()) { + Log.w( + LOG_TAG, + "Some platform-signed non-system packages don't have their requested platform" + + " signature permissions granted. Suggested signature permission allowlist" + + " additions:\n\n" + + buildSiganturePermissionAllowlist(deniedPackageAndPermissions), + ) + } + } + + private fun getPlatformSignaturePermissionNames(): List<String> = + packageManager + .getPackageInfo("android", PackageManager.GET_PERMISSIONS) + .permissions!! + .filter { it.protection == PermissionInfo.PROTECTION_SIGNATURE } + .map { it.name } + + private fun getPlatformSignedNonSystemPackageInfos(): List<PackageInfo> = + packageManager + .getInstalledPackages(PackageManager.GET_PERMISSIONS) + .filter { + it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 || + it.applicationInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP != 0 + } + .filter { PermissionUtils.isPlatformSigned(it.packageName) } + + private fun buildSiganturePermissionAllowlist( + packagesAndPermissions: Map<String, List<String>> + ): String = buildString { + for ((packageName, permissionNames) in packagesAndPermissions) { + append(" <signature-permissions package=\"$packageName\">\n") + for (permissionName in permissionNames) { + append(" <permission name=\"$permissionName\" />\n") + } + append(" </signature-permissions>\n") + } + } + + companion object { + private val LOG_TAG = SignaturePermissionAllowlistConfigTest::class.java.simpleName + } +} diff --git a/tests/cts/permissionui/Android.bp b/tests/cts/permissionui/Android.bp index c4ff0e069..c047b397d 100644 --- a/tests/cts/permissionui/Android.bp +++ b/tests/cts/permissionui/Android.bp @@ -44,6 +44,7 @@ android_test { "platform-test-annotations", "android.content.pm.flags-aconfig-java-export", "android.permission.flags-aconfig-java-export", + "com.android.permission.flags-aconfig-java-export", ], data: [ ":CtsPermissionPolicyApp25", diff --git a/tests/cts/permissionui/AndroidManifest.xml b/tests/cts/permissionui/AndroidManifest.xml index 3b80b8d8b..b5c9e2ad0 100644 --- a/tests/cts/permissionui/AndroidManifest.xml +++ b/tests/cts/permissionui/AndroidManifest.xml @@ -25,6 +25,7 @@ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> + <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" /> <application> @@ -78,6 +79,23 @@ <meta-data android:name="android.accessibilityservice" android:resource="@xml/test_accessibilityservice"/> </service> + <service android:name=".VoipHelperTestConnectionService" + android:exported="true" + android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"> + <intent-filter> + <action android:name="android.telecom.ConnectionService" /> + </intent-filter> + </service> + + <service android:name=".EcmInCallTestInCallService" + android:permission="android.permission.BIND_INCALL_SERVICE" + android:exported="true"> + <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS" + android:value="true" /> + <intent-filter> + <action android:name="android.telecom.InCallService"/> + </intent-filter> + </service> </application> diff --git a/tests/cts/permissionui/AndroidTest.xml b/tests/cts/permissionui/AndroidTest.xml index 187129d30..d5d8a27cf 100644 --- a/tests/cts/permissionui/AndroidTest.xml +++ b/tests/cts/permissionui/AndroidTest.xml @@ -93,6 +93,12 @@ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionui"/> </target_preparer> + <!-- Ensures that screen recording is captured --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="true" /> + <option name="throw-on-error" value="false" /> + </target_preparer> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/data/user/0/android.permissionui.cts/files" /> <option name="collect-on-run-ended-only" value="true" /> diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt index 4ada5717e..f0c12171c 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt @@ -335,7 +335,10 @@ class AppPermissionTest : BaseUsePermissionTest() { } private fun assertAppPermissionRationaleContainerIsVisible(expected: Boolean) { - findView(By.res(APP_PERMISSION_RATIONALE_CONTAINER_VIEW), expected) + findView( + By.text(getPermissionControllerString(APP_PERMISSION_RATIONALE_TITLE_TEXT)), + expected + ) } companion object { diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt index 842663308..92599b617 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt @@ -122,8 +122,6 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { const val ALLOW_ALWAYS_RADIO_BUTTON = "com.android.permissioncontroller:id/allow_always_radio_button" - const val ALLOW_RADIO_BUTTON_FRAME = - "com.android.permissioncontroller:id/allow_radio_button_frame" const val ALLOW_RADIO_BUTTON = "com.android.permissioncontroller:id/allow_radio_button" const val ALLOW_FOREGROUND_RADIO_BUTTON = "com.android.permissioncontroller:id/allow_foreground_only_radio_button" @@ -147,10 +145,9 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { const val ECM_ALERT_DIALOG_OK_BUTTON_TEXT = "enhanced_confirmation_dialog_ok" const val ALERT_DIALOG_MESSAGE = "android:id/message" const val ALERT_DIALOG_OK_BUTTON = "android:id/button1" - const val APP_PERMISSION_RATIONALE_CONTAINER_VIEW = - "com.android.permissioncontroller:id/app_permission_rationale_container" - const val APP_PERMISSION_RATIONALE_CONTENT_VIEW = - "com.android.permissioncontroller:id/app_permission_rationale_content" + const val APP_PERMISSION_RATIONALE_TITLE_TEXT = "app_location_permission_rationale_title" + const val APP_PERMISSION_RATIONALE_SUBTITLE_TEXT = + "app_location_permission_rationale_subtitle" const val GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW = "com.android.permissioncontroller:id/permission_rationale_container" const val PERMISSION_RATIONALE_ACTIVITY_TITLE_VIEW = @@ -1013,7 +1010,9 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { protected fun clickPermissionRationaleContentInAppPermission() { clickAndWaitForWindowTransition( - By.res(APP_PERMISSION_RATIONALE_CONTENT_VIEW).displayId(displayId)) + By.text(getPermissionControllerString(APP_PERMISSION_RATIONALE_SUBTITLE_TEXT)) + .displayId(displayId) + ) } protected fun clickPermissionRationaleViewInGrantDialog() { @@ -1125,6 +1124,21 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } } + @Suppress("DEPRECATION") + protected fun startManageAppPermissionsActivity() { + doAndWaitForWindowTransition { + runWithShellPermissionIdentity { + context.startActivity( + Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME) + } + ) + } + } + } + /** Starts activity with intent [ACTION_REVIEW_APP_DATA_SHARING_UPDATES]. */ fun startAppDataSharingUpdatesActivity() { doAndWaitForWindowTransition { diff --git a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt index 29e6f231b..7abea1f23 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt @@ -28,13 +28,16 @@ import android.os.Build import android.os.Process import android.os.SystemClock import android.os.SystemProperties +import android.os.UserManager import android.permission.PermissionManager +import android.permission.cts.MtsIgnore import android.platform.test.annotations.AsbSecurityTest import android.platform.test.rule.ScreenRecordRule import android.provider.DeviceConfig import android.provider.Settings import android.safetycenter.SafetyCenterManager import android.server.wm.WindowManagerStateHelper +import android.util.Log import androidx.annotation.RequiresApi import androidx.test.filters.FlakyTest import androidx.test.filters.SdkSuppress @@ -86,8 +89,7 @@ private const val PRIVACY_ITEM_ID = "com.android.systemui:id/privacy_item" private const val INDICATORS_FLAG = "camera_mic_icons_enabled" private const val WEAR_MIC_LABEL = "Microphone" private const val PERMISSION_INDICATORS_NOT_PRESENT = 162547999L -private const val IDLE_TIMEOUT_MILLIS: Long = 1000 -private const val UNEXPECTED_TIMEOUT_MILLIS = 1000L +private const val IDLE_TIMEOUT_MILLIS: Long = 2000 private const val TIMEOUT_MILLIS: Long = 20000 private const val TV_MIC_INDICATOR_WINDOW_TITLE = "MicrophoneCaptureIndicator" private const val MIC_LABEL_NAME = "microphone_toggle_label_qs" @@ -138,6 +140,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { private const val AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS = 30_000L const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled" const val DELAY_MILLIS = 3000L + private val TAG = CameraMicIndicatorsPermissionTest::class.java.simpleName } private val safetyCenterEnabled = callWithShellPermissionIdentity { @@ -281,6 +284,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { @Test @AsbSecurityTest(cveBugId = [258672042]) + @MtsIgnore(bugId = 351903707) fun testMicIndicatorWithManualFinishOpStillShows() { testCameraAndMicIndicator( useMic = true, @@ -374,6 +378,11 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { safetyCenterEnabled: Boolean = false, finishEarly: Boolean = false ) { + Log.d( + TAG, + "testCameraAndMicIndicator useMic=$useMic useCamera=$useCamera " + + "safetyCenterEnabled=$safetyCenterEnabled finishEarly=$finishEarly" + ) // If camera is not available skip the test if (useCamera) { assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) @@ -407,6 +416,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { } } + Log.d(TAG, "assert to make sure Indicators are displayed") assertIndicatorsShown(useMic, useCamera, useHotword, chainUsage, safetyCenterEnabled) if (finishEarly) { @@ -415,10 +425,13 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { try { // Check if the indicator has gone away. This will throw an exception if the // indicator is still present + Log.d(TAG, "checking on indicators again") assertIndicatorsShown(false, false, false) + Log.d(TAG, "indicators are gone") // If we successfully asserted that the indicator went away, fail the test failed = true } catch (t: Throwable) { + Log.d(TAG, "indicators are continuing to show") // expected } if (failed) { @@ -458,6 +471,13 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { } else { useMic } + if (!micInUse && !useCamera) { + // We're asserting the indicator is gone. Wait up to IDLE_TIMEOUT after the quick + // settings is opened to see if we find the indicator, so we don't automatically + // assert the indicator is gone, just because we didn't open quick settings fast + // enough. + UiAutomatorUtils2.waitFindObjectOrNull(By.res(PRIVACY_CHIP_ID), IDLE_TIMEOUT_MILLIS) + } assertPrivacyChipAndIndicatorsPresent( micInUse, useCamera, @@ -588,6 +608,7 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { privacyChip.click() } } else { + Log.d(TAG, "waiting for PRIVACY_CHIP_ID to disappear") assertWithUiDump { UiAutomatorUtils2.waitUntilObjectGone(By.res(PRIVACY_CHIP_ID)) } return } @@ -762,4 +783,10 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase { private fun byOneOfText(vararg textValues: String) = By.text(Pattern.compile(textValues.joinToString(separator = "|") { Pattern.quote(it) })) + + fun isAutomotiveWithVisibleBackgroundUser(): Boolean { + val userManager = context.getSystemService(UserManager::class.java) + return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) && + userManager.isVisibleBackgroundUsersSupported() + } } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt new file mode 100644 index 000000000..f38f678e9 --- /dev/null +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permissionui.cts + +import android.Manifest.permission_group.SMS +import android.os.Build +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.test.filters.SdkSuppress +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags +import org.junit.Assume.assumeFalse +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +/** Enhanced Confirmation Backport UI tests. */ +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") +class EnhancedConfirmationBackportTest : BaseUsePermissionTest() { + + @JvmField + @Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Before + fun setup() { + assumeFalse(isAutomotive) + assumeFalse(isTv) + assumeFalse(isWatch) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installDownloadedFile_clickAppPermissions_clickAllowRestrictedSettings_clickSMSPermGroup_clickAllowed() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).clickAndWait( + Until.newWindow(), + BasePermissionTest.TIMEOUT_MILLIS + ) + + if (!SdkLevel.isAtLeastV()) { + waitFindObject(By.text(ALLOW_RESTRICTED_SETTINGS)).click() + + pressBack() + + navigateToIndividualPermissionSetting(SMS) + + assertAllowButtonIsEnabledAndClickAndChecked() + + pressBack() + } else { + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + } + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installFromLocalFile_clickAppPermissions_clickAllowRestrictedSettings_clickSMSPermGroup_clickAllowed() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).click() + + if (!SdkLevel.isAtLeastV()) { + waitFindObject(By.text(ALLOW_RESTRICTED_SETTINGS)).click() + + pressBack() + + navigateToIndividualPermissionSetting(SMS) + + assertAllowButtonIsEnabledAndClickAndChecked() + + pressBack() + } else { + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + } + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installDownloadedFile_clickAppPermissions_noAllowRestrictedSettings() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).clickAndWait( + Until.newWindow(), + BasePermissionTest.TIMEOUT_MILLIS + ) + + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installFromLocalFile_clickAppPermissions_noAllowRestrictedSettings() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).click() + + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + + pressBack() + } + + private fun assertAllowButtonIsEnabledAndClickAndChecked() { + waitFindObject(By.res(ALLOW_RADIO_BUTTON).enabled(true).checked(false)) + .click() + waitFindObject(By.res(ALLOW_RADIO_BUTTON).checked(true)) + } + + companion object { + private const val MORE_OPTIONS = "More options" + private const val ALLOW_RESTRICTED_SETTINGS = "Allow restricted settings" + } +} diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt new file mode 100644 index 000000000..c410f9c9c --- /dev/null +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permissionui.cts + +import android.app.AppOpsManager +import android.app.Instrumentation +import android.app.ecm.EnhancedConfirmationManager +import android.content.ContentProviderOperation +import android.content.Context +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.permission.flags.Flags +import android.platform.test.annotations.AppModeFull +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.provider.ContactsContract +import android.provider.ContactsContract.CommonDataKinds +import android.provider.ContactsContract.Data +import android.provider.ContactsContract.RawContacts +import androidx.test.filters.SdkSuppress +import androidx.test.platform.app.InstrumentationRegistry +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity +import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity +import org.junit.After +import org.junit.AfterClass +import org.junit.Assert +import org.junit.Assume +import org.junit.Before +import org.junit.BeforeClass +import org.junit.Rule +import org.junit.Test + +/** + * This test verifies the behavior of the Enhanced Confirmation Manager APIs that deal with unknown + * callers + */ +@AppModeFull(reason = "Instant apps cannot install packages") +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") +@RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_IN_CALL_APIS_ENABLED) +// @CddTest(requirement = "TBD") +class EnhancedConfirmationInCallTest { + private val ecm = context.getSystemService(EnhancedConfirmationManager::class.java)!! + private val packageManager = context.packageManager + private val addedContacts = mutableMapOf<String, List<Uri>>() + + @JvmField + @Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Before + fun assumeNotAutoOrTv() { + Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) + } + + companion object { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val context: Context = instrumentation.targetContext + private lateinit var voipService: VoipCallHelper + + @JvmStatic + @BeforeClass + fun setupVoipService() { + voipService = VoipCallHelper(context) + voipService.registerPhoneAccount() + } + + @JvmStatic + @AfterClass + fun tearDownVoipService() { + voipService.removePhoneAccount() + } + + const val CONTACT_DISPLAY_NAME = "Alice Bobson" + const val NON_CONTACT_DISPLAY_NAME = "Eve McEve" + const val CONTACT_PHONE_NUMBER = "8888888888" + const val NON_CONTACT_PHONE_NUMBER = "1111111111" + } + + private fun addContact(displayName: String, phoneNumber: String) { + runWithShellPermissionIdentity { + val ops: ArrayList<ContentProviderOperation> = ArrayList() + ops.add( + ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) + .withValue(RawContacts.ACCOUNT_TYPE, "test type") + .withValue(RawContacts.ACCOUNT_NAME, "test account") + .build() + ) + ops.add( + ContentProviderOperation.newInsert(Data.CONTENT_URI) + .withValueBackReference(Data.RAW_CONTACT_ID, 0) + .withValue(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .build() + ) + ops.add( + ContentProviderOperation.newInsert(Data.CONTENT_URI) + .withValueBackReference(Data.RAW_CONTACT_ID, 0) + .withValue(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Phone.NUMBER, phoneNumber) + .build() + ) + val results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) + val resultsForDisplayName = mutableListOf<Uri>() + results.forEach { resultsForDisplayName.add(it.uri!!) } + addedContacts[displayName] = resultsForDisplayName + } + } + + private fun removeContact(displayName: String) { + runWithShellPermissionIdentity { + var totalRowsRemoved = 0 + for (data in addedContacts[displayName] ?: emptyList()) { + totalRowsRemoved += context.contentResolver.delete(data, null) + } + // There are multiple contacts tables, and removing from the raw_contacts table + // can cause row removals from the data table, so we may get some uris that don't + // report a delete, but we should get at least one, and not more than the number of uris + Assert.assertNotEquals( + "Expected at least one contact row to be removed", + 0, + totalRowsRemoved, + ) + Assert.assertTrue( + "Unexpectedly large number of contact rows removed", + totalRowsRemoved <= (addedContacts[displayName]?.size ?: 0), + ) + addedContacts.remove(displayName) + } + } + + @After + fun tearDown() { + voipService.endCallAndWaitForInactive() + addedContacts.keys.forEach { removeContact(it) } + } + + private fun isSettingRestricted(): Boolean { + return callWithShellPermissionIdentity { + ecm.isRestricted(context.packageName, AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES) + } + } + + @Test + fun testIncomingCall_NonContact() { + voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, NON_CONTACT_PHONE_NUMBER) + Assert.assertTrue(isSettingRestricted()) + voipService.endCallAndWaitForInactive() + Assert.assertFalse(isSettingRestricted()) + } + + @Test + fun testIncomingCall_Contact_DisplayNameMatches_PhoneNotGiven() { + addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) + // If no phone number is given, the display name will be checked + voipService.createCallAndWaitForActive(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) + Assert.assertFalse(isSettingRestricted()) + voipService.endCallAndWaitForInactive() + Assert.assertFalse(isSettingRestricted()) + } + + @Test + fun testIncomingCall_Contact_PhoneNumberMatches() { + addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) + // If the phone number matches, the display name is not checked + voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) + Assert.assertFalse(isSettingRestricted()) + voipService.endCallAndWaitForInactive() + Assert.assertFalse(isSettingRestricted()) + } + + @Test + fun testCall_DoesntBecomeTrustedIfCallerAddedDuringCall() { + val tempContactDisplay = "TEMP CONTACT" + val tempContactPhone = "999-999-9999" + voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone) + addContact(tempContactDisplay, tempContactPhone) + // State should not be recomputed just because the contact is newly added + Assert.assertTrue(isSettingRestricted()) + voipService.endCallAndWaitForInactive() + voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone) + // A new call should recognize our contact, and mark the call as trusted + Assert.assertFalse(isSettingRestricted()) + } +} diff --git a/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt index c8298b19a..75a8914bf 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt @@ -141,7 +141,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() { launchApp() killTestApp() launchApp() - waitFindObject(By.textContains(ALLOW)) + waitFindObject(By.textContains(ALLOW).displayId(displayId)) clickPermissionRequestAllowButton() } @@ -201,9 +201,10 @@ class NotificationPermissionTest : BaseUsePermissionTest() { installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true) launchApp(startSecondActivity = true) if (isAutomotive || isWatch) { - waitFindObject(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT))) + waitFindObject( + By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)).displayId(displayId)) } else { - waitFindObject(By.res(ALLOW_BUTTON)) + waitFindObject(By.res(ALLOW_BUTTON).displayId(displayId)) } pressBack() clickPermissionRequestAllowButton() @@ -239,7 +240,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() { try { // Watch does not have app bar if (!isWatch) { - waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL)) + waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL).displayId(displayId)) } assertDialogNotShowing() } finally { @@ -400,7 +401,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() { // Watch does not have app bar if (!isWatch) { - waitFindObject(By.textContains(ACTIVITY_LABEL)) + waitFindObject(By.textContains(ACTIVITY_LABEL).displayId(displayId)) } } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt index d509add3a..da70fc186 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt @@ -17,15 +17,22 @@ package android.permissionui.cts import android.os.Build +import android.permission.flags.Flags +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.filters.FlakyTest import androidx.test.filters.SdkSuppress import org.junit.Assume.assumeFalse import org.junit.Before +import org.junit.Rule import org.junit.Test /** Runtime permission behavior tests for permission splits. */ @FlakyTest class PermissionSplitTest : BaseUsePermissionTest() { + + @Rule @JvmField val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before fun assumeNotTv() { assumeFalse(isTv) @@ -56,6 +63,7 @@ class PermissionSplitTest : BaseUsePermissionTest() { } @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) @Test fun testBodySensorSplit() { installPackage(APP_APK_PATH_31) @@ -63,6 +71,7 @@ class PermissionSplitTest : BaseUsePermissionTest() { } @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) @Test fun testBodySensorSplit32() { installPackage(APP_APK_PATH_32) @@ -70,6 +79,7 @@ class PermissionSplitTest : BaseUsePermissionTest() { } @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) @Test fun testBodySensorNonSplit() { installPackage(APP_APK_PATH_LATEST) diff --git a/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt index e434e9c70..55f028e17 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt @@ -190,7 +190,7 @@ class ReviewAccessibilityServicesTest { !uiDevice.performActionAndWait( { block() }, Until.newWindow(), - NEW_WINDOW_TIMEOUT_MILLIS + NEW_WINDOW_TIMEOUT_MILLIS, ) if (timeoutOccurred) { @@ -212,17 +212,11 @@ class ReviewAccessibilityServicesTest { private fun waitForSettingsButtonToDisappear() { SystemUtil.eventually { - findPCObjectByClassAndText(false, - "android.widget.Button", - "Settings" - ) + findPCObjectByClassAndText(false, "android.widget.Button", "Settings") } } - private fun findObjectByTextWithoutRetry( - shouldBePresent: Boolean, - text: String, - ): UiObject2? { + private fun findObjectByTextWithoutRetry(shouldBePresent: Boolean, text: String): UiObject2? { val containsWithoutCaseSelector = By.text(Pattern.compile(".*$text.*", Pattern.CASE_INSENSITIVE)) val view = @@ -235,7 +229,7 @@ class ReviewAccessibilityServicesTest { assertEquals( "Expected to find view with text $text: $shouldBePresent", shouldBePresent, - view != null + view != null, ) return view } @@ -251,15 +245,16 @@ class ReviewAccessibilityServicesTest { private fun findPCObjectByClassAndText( shouldBePresent: Boolean, className: String, - text: String + text: String, ): UiObject2? { - val selector = By.pkg(packageName) - .clazz(className) - .text(text) + val selector = By.pkg(packageName).clazz(className).text(text) val view = waitFindObjectOrNull(selector) assertEquals( "Expected to find view with packageName '$packageName' className '$className' " + - "text '$text' : $shouldBePresent", shouldBePresent, view != null) + "text '$text' : $shouldBePresent", + shouldBePresent, + view != null, + ) return view } } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt b/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt new file mode 100644 index 000000000..480d7bff3 --- /dev/null +++ b/tests/cts/permissionui/src/android/permissionui/cts/VoipCallHelper.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permissionui.cts + +import android.content.ComponentName +import android.content.Context +import android.net.Uri +import android.os.Bundle +import android.os.Process +import android.permissionui.cts.VoipCallHelper.Companion.EXTRA_DISPLAY_NAME +import android.permissionui.cts.VoipCallHelper.Companion.awaitingCallStateLatch +import android.permissionui.cts.VoipCallHelper.Companion.currentActiveConnection +import android.telecom.Connection +import android.telecom.ConnectionRequest +import android.telecom.ConnectionService +import android.telecom.DisconnectCause +import android.telecom.PhoneAccount +import android.telecom.PhoneAccountHandle +import android.telecom.TelecomManager +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.Assert + +/** A helper class which can register a phone account, and make/end VOIP phone calls */ +class VoipCallHelper(val context: Context) { + private val telecomManager by lazy { context.getSystemService(TelecomManager::class.java) } + private lateinit var phoneAccount: PhoneAccount + private val accountHandle = + PhoneAccountHandle( + ComponentName(context, VoipHelperTestConnectionService::class.java), + "cts-voip-helper-test", + Process.myUserHandle(), + ) + + init { + registerPhoneAccount() + } + + companion object { + var currentActiveConnection: VoIPConnection? = null + var awaitingCallStateLatch: CallPlacedLatch? = null + + const val EXTRA_DISPLAY_NAME = "display_name" + const val CUSTOM_ADDRESS_SCHEMA = "custom_schema" + const val CALL_STATE_WAIT_MS = 1000L + const val CALL_TIMEOUT_MS = 10000L + } + + fun registerPhoneAccount() { + val phoneAccountBuilder = PhoneAccount.builder(accountHandle, "CTS VOIP HELPER") + phoneAccountBuilder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) + // see b/343674176. Some OEMs expect the PhoneAccount.getExtras() to be non-null + val defaultBundle = Bundle() + phoneAccountBuilder.setExtras(defaultBundle) + + // build and register the PhoneAccount via the Platform API + phoneAccount = phoneAccountBuilder.build() + telecomManager.registerPhoneAccount(phoneAccount) + } + + fun removePhoneAccount() { + telecomManager.unregisterPhoneAccount(phoneAccount.accountHandle) + } + + fun createCallAndWaitForActive(displayName: String?, phoneNumber: String?) { + val extras = Bundle() + + val phoneUri = + if (phoneNumber != null) { + Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null) + } else { + // If we don't have a phone number, provide a custom address URI, like many VOIP + // apps that aren't tied to a phone number do + Uri.fromParts(CUSTOM_ADDRESS_SCHEMA, "custom_address", null) + } + if (displayName != null) { + extras.putString(EXTRA_DISPLAY_NAME, displayName) + } + extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, phoneUri) + awaitingCallStateLatch = CallPlacedLatch(phoneUri, displayName) + telecomManager.addNewIncomingCall(phoneAccount.accountHandle, extras) + Assert.assertTrue( + "Timed out waiting for call to start", + awaitingCallStateLatch!!.await(CALL_TIMEOUT_MS, TimeUnit.MILLISECONDS), + ) + // TODO b/379941144: Replace wait with waiting until a test InCallService gets a callback + Thread.sleep(CALL_STATE_WAIT_MS) + } + + fun endCallAndWaitForInactive() { + currentActiveConnection?.let { connection -> + connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) + connection.destroy() + // TODO b/379941144: Replace wait with waiting until a test InCallService gets a + // callback + Thread.sleep(CALL_STATE_WAIT_MS) + } + currentActiveConnection = null + } +} + +class CallPlacedLatch(val address: Uri?, val displayName: String?) : CountDownLatch(1) { + fun nameAndNumberMatch(connection: Connection): Boolean { + return connection.address == address && connection.callerDisplayName == displayName + } +} + +class VoIPConnection : Connection() { + init { + setConnectionProperties(PROPERTY_SELF_MANAGED) + setAudioModeIsVoip(true) + setActive() + } + + override fun onShowIncomingCallUi() { + super.onShowIncomingCallUi() + setActive() + currentActiveConnection = this + if (awaitingCallStateLatch?.nameAndNumberMatch(this) == true) { + awaitingCallStateLatch?.countDown() + } + } +} + +class VoipHelperTestConnectionService : ConnectionService() { + override fun onCreateOutgoingConnection( + connectionManagerPhoneAccount: PhoneAccountHandle, + request: ConnectionRequest, + ): Connection { + return createConnection(request) + } + + override fun onCreateIncomingConnection( + connectionManagerPhoneAccount: PhoneAccountHandle?, + request: ConnectionRequest?, + ): Connection { + return createConnection(request) + } + + private fun createConnection(request: ConnectionRequest?): Connection { + val connection = VoIPConnection() + if (request?.extras?.containsKey(EXTRA_DISPLAY_NAME) == true) { + connection.setCallerDisplayName( + request.extras.getString(EXTRA_DISPLAY_NAME), + TelecomManager.PRESENTATION_ALLOWED, + ) + connection.setAddress( + request.extras.getParcelable( + TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, + Uri::class.java, + ), + TelecomManager.PRESENTATION_ALLOWED, + ) + } + return connection + } +} diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp index e392109db..9f1e6cff6 100644 --- a/tests/cts/role/Android.bp +++ b/tests/cts/role/Android.bp @@ -30,9 +30,12 @@ android_test { static_libs: [ "android.permission.flags-aconfig-java-export", "androidx.test.rules", + "com.android.permission.flags-aconfig-java-export", "compatibility-device-util-axt", "ctstestrunner-axt", "Harrier", + "bedstead-multiuser", + "flag-junit", "platform-test-annotations", "truth", ], @@ -48,6 +51,6 @@ android_test { ":CtsRoleTestApp", ":CtsRoleTestApp28", ":CtsRoleTestApp33WithoutInCallService", - ":CtsRoleTestAppForProfile", + ":CtsRoleTestAppClone", ], } diff --git a/tests/cts/role/AndroidTest.xml b/tests/cts/role/AndroidTest.xml index 25916ebce..73f23dd1b 100644 --- a/tests/cts/role/AndroidTest.xml +++ b/tests/cts/role/AndroidTest.xml @@ -43,7 +43,7 @@ <option name="push" value="CtsRoleTestApp.apk->/data/local/tmp/cts-role/CtsRoleTestApp.apk" /> <option name="push" value="CtsRoleTestApp28.apk->/data/local/tmp/cts-role/CtsRoleTestApp28.apk" /> <option name="push" value="CtsRoleTestApp33WithoutInCallService.apk->/data/local/tmp/cts-role/CtsRoleTestApp33WithoutInCallService.apk" /> - <option name="push" value="CtsRoleTestAppForProfile.apk->/data/local/tmp/cts-role/CtsRoleTestAppForProfile.apk" /> + <option name="push" value="CtsRoleTestAppClone.apk->/data/local/tmp/cts-role/CtsRoleTestAppClone.apk" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/tests/cts/role/CtsRoleTestAppForProfile/Android.bp b/tests/cts/role/CtsRoleTestAppClone/Android.bp index 1d9a1e6e8..0ac48da8b 100644 --- a/tests/cts/role/CtsRoleTestAppForProfile/Android.bp +++ b/tests/cts/role/CtsRoleTestAppClone/Android.bp @@ -17,7 +17,7 @@ package { } android_test_helper_app { - name: "CtsRoleTestAppForProfile", + name: "CtsRoleTestAppClone", defaults: ["mts-target-sdk-version-current"], min_sdk_version: "30", } diff --git a/tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml b/tests/cts/role/CtsRoleTestAppClone/AndroidManifest.xml index 05413e55f..f45ab359b 100644 --- a/tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml +++ b/tests/cts/role/CtsRoleTestAppClone/AndroidManifest.xml @@ -18,9 +18,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.app.role.cts.appForProfile"> + package="android.app.role.cts.appClone"> - <application android:label="CtsRoleTestAppForProfile"> + <application android:label="CtsRoleTestAppClone"> <!-- Browser --> <activity android:name=".BrowserActivity" diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java index 401bc097b..ce1c26d2d 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java +++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java @@ -16,6 +16,7 @@ package android.app.role.cts; +import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.privateProfile; import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow; @@ -44,6 +45,7 @@ import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.permission.flags.Flags; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -66,7 +68,7 @@ import androidx.test.uiautomator.Until; import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.bedstead.harrier.DeviceState; -import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile; +import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile; import com.android.bedstead.nene.types.OptionalBoolean; import com.android.compatibility.common.util.DisableAnimationRule; import com.android.compatibility.common.util.FreezeRotationRule; @@ -103,15 +105,17 @@ public class RoleManagerTest { private static final String ROLE_NAME = RoleManager.ROLE_BROWSER; private static final String ROLE_PHONE_NAME = RoleManager.ROLE_DIALER; private static final String ROLE_SMS_NAME = RoleManager.ROLE_SMS; + private static final String PROFILE_GROUP_EXCLUSIVE_ROLE_NAME = + RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY; private static final String ROLE_SHORT_LABEL = "Browser app"; private static final String APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk"; private static final String APP_PACKAGE_NAME = "android.app.role.cts.app"; private static final String APP_LABEL = "CtsRoleTestApp"; - private static final String APP_FOR_PROFILE_APK_PATH = - "/data/local/tmp/cts-role/CtsRoleTestAppForProfile.apk"; - private static final String APP_FOR_PROFILE_PACKAGE_NAME = "android.app.role.cts.appForProfile"; - private static final String APP_FOR_PROFILE = "CtsRoleTestAppForProfile"; + private static final String APP_CLONE_APK_PATH = + "/data/local/tmp/cts-role/CtsRoleTestAppClone.apk"; + private static final String APP_CLONE_PACKAGE_NAME = "android.app.role.cts.appClone"; + private static final String APP_CLONE = "CtsRoleTestAppClone"; private static final String APP_IS_ROLE_HELD_ACTIVITY_NAME = APP_PACKAGE_NAME + ".IsRoleHeldActivity"; private static final String APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD = APP_PACKAGE_NAME @@ -183,7 +187,21 @@ public class RoleManagerTest { private String mRoleHolder; @Before - public void saveRoleHolder() throws Exception { + public void setUp() throws Exception { + assumeTrue(RoleManagerUtil.INSTANCE.isCddCompliantScreenSize()); + saveRoleHolder(); + installApp(); + wakeUpScreen(); + closeNotificationShade(); + } + + @After + public void tearDown() throws Exception { + uninstallApp(); + restoreRoleHolder(); + } + + private void saveRoleHolder() throws Exception { List<String> roleHolders = getRoleHolders(ROLE_NAME); mRoleHolder = !roleHolders.isEmpty() ? roleHolders.get(0) : null; @@ -193,8 +211,7 @@ public class RoleManagerTest { } } - @After - public void restoreRoleHolder() throws Exception { + private void restoreRoleHolder() throws Exception { removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); if (mRoleHolder != null) { @@ -204,27 +221,27 @@ public class RoleManagerTest { assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); } - @Before - public void installApp() throws Exception { + private void installApp() throws Exception { installPackage(APP_APK_PATH); installPackage(APP_28_APK_PATH); installPackage(APP_33_WITHOUT_INCALLSERVICE_APK_PATH); + // Install CtsRoleTestAppClone as default role holder for browser role + // in case no browser is installed on system + installPackage(APP_CLONE_APK_PATH); } - @After - public void uninstallApp() throws Exception { + private void uninstallApp() throws Exception { uninstallPackage(APP_PACKAGE_NAME); uninstallPackage(APP_28_PACKAGE_NAME); uninstallPackage(APP_33_WITHOUT_INCALLSERVICE_PACKAGE_NAME); + uninstallPackage(APP_CLONE_PACKAGE_NAME); } - @Before - public void wakeUpScreen() throws IOException { + private void wakeUpScreen() throws IOException { runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP"); } - @Before - public void closeNotificationShade() { + private void closeNotificationShade() { sContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } @@ -852,25 +869,27 @@ public class RoleManagerTest { return; } - UserHandle privateProfile = sDeviceState.privateProfile().userHandle(); + UserHandle privateProfile = privateProfile(sDeviceState).userHandle(); assertThat(privateProfile).isNotNull(); installPackage(APP_APK_PATH, privateProfile); - installPackage(APP_FOR_PROFILE_APK_PATH, privateProfile); + installPackage(APP_CLONE_APK_PATH, privateProfile); UiAutomatorUtils.getUiDevice().waitForIdle(30 * 1000); - addRoleHolderAsUser(ROLE_NAME, APP_FOR_PROFILE_PACKAGE_NAME, privateProfile); + addRoleHolderAsUser(ROLE_NAME, APP_CLONE_PACKAGE_NAME, privateProfile); sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS) .addCategory(Intent.CATEGORY_DEFAULT) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); waitForIdle(); - waitFindObject(By.hasDescendant(By.text(APP_FOR_PROFILE))).click(); + waitFindObject(By.hasDescendant(By.text(APP_CLONE))).click(); waitForIdle(); waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false)) .hasDescendant(By.text(APP_LABEL))).click(); + waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true)) + .hasDescendant(By.text(APP_LABEL))); assertIsRoleHolderAsUser(ROLE_NAME, APP_PACKAGE_NAME, true, privateProfile); @@ -878,7 +897,7 @@ public class RoleManagerTest { pressBack(); uninstallPackage(APP_PACKAGE_NAME, privateProfile); - uninstallPackage(APP_FOR_PROFILE_APK_PATH, privateProfile); + uninstallPackage(APP_CLONE_PACKAGE_NAME, privateProfile); } @Test @@ -1336,6 +1355,287 @@ public class RoleManagerTest { }); } + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetActiveUserForRoleWithoutPermission() throws Exception { + assertThrows(SecurityException.class, () -> + sRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetActiveUserForNonProfileGroupExclusiveRole() throws Exception { + runWithShellPermissionIdentity(() -> + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.getActiveUserForRole( + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER))); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetActiveUserForRoleWithoutPermission() throws Exception { + assertThrows(SecurityException.class, () -> + sRoleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + Process.myUserHandle(), 0)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetActiveUserForNonProfileGroupExclusiveRole() throws Exception { + runWithShellPermissionIdentity(() -> + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.setActiveUserForRole( + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, Process.myUserHandle(), + 0))); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetActiveUserForRole() throws Exception { + runWithShellPermissionIdentity(() -> { + sRoleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + Process.myUserHandle(), 0); + assertThat(sRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)) + .isEqualTo(Process.myUserHandle()); + }); + } + + @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetDefaultHoldersForTestFlagDisabled() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalStateException.class, () -> + sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetDefaultHoldersForTestNoPermissions() throws Exception { + assertThrows(SecurityException.class, () -> + sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetDefaultHoldersForTestEmptyRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.getDefaultHoldersForTest("")); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetDefaultHoldersForTestNonTestRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.getDefaultHoldersForTest( + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)); + }); + } + + @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetDefaultHoldersForTestFlagDisabled() throws Exception { + List<String> testRoleHolders = List.of("a", "b", "c"); + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalStateException.class, () -> + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + testRoleHolders)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetDefaultHoldersForTestNoPermissions() throws Exception { + List<String> testRoleHolders = List.of("a", "b", "c"); + assertThrows(SecurityException.class, () -> + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + testRoleHolders)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetDefaultHoldersForTestEmptyRoleName() throws Exception { + List<String> testRoleHolders = List.of("a", "b", "c"); + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.setDefaultHoldersForTest("", testRoleHolders)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetDefaultHoldersForTestNonTestRoleName() throws Exception { + List<String> testRoleHolders = List.of("a", "b", "c"); + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.setDefaultHoldersForTest( + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, testRoleHolders)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetDefaultHolders() throws Exception { + List<String> testRoleHolders = List.of("a", "b", "c"); + runWithShellPermissionIdentity(() -> { + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + testRoleHolders); + List<String> roleHolders = + sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME); + assertThat(roleHolders).isEqualTo(testRoleHolders); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetDefaultHoldersNoRoleHolders() throws Exception { + List<String> initialRoleHolders = List.of("a", "b", "c"); + List<String> testRoleHolders = Collections.emptyList(); + runWithShellPermissionIdentity(() -> { + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + initialRoleHolders); + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + testRoleHolders); + List<String> roleHolders = + sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME); + assertThat(roleHolders).isEqualTo(testRoleHolders); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetDefaultHoldersNullRoleHolders() throws Exception { + List<String> initialRoleHolders = List.of("a", "b", "c"); + runWithShellPermissionIdentity(() -> { + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, + initialRoleHolders); + sRoleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, null); + List<String> roleHolders = + sRoleManager.getDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME); + assertThat(roleHolders).isEqualTo(Collections.emptyList()); + }); + } + + @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetIsRoleVisibleForTestFlagDisabled() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalStateException.class, () -> + sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetIsRoleVisibleForTestNoPermissions() throws Exception { + assertThrows(SecurityException.class, () -> + sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetIsRoleVisibleForTestEmptyRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.isRoleVisibleForTest("")); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotGetIsRoleVisibleForTestNonTestRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.isRoleVisibleForTest(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)); + }); + } + + @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetRoleVisibleForTestFlagDisabled() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalStateException.class, () -> + sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetRoleVisibleForTestNoPermissions() throws Exception { + assertThrows(SecurityException.class, () -> + sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false)); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetRoleVisibleForTestEmptyRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.setRoleVisibleForTest("", false)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void cannotSetRoleVisibleForTestNonTestRoleName() throws Exception { + runWithShellPermissionIdentity(() -> { + assertThrows(IllegalArgumentException.class, () -> + sRoleManager.setRoleVisibleForTest(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, + false)); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetIsRoleVisibleForTestSetTrue() throws Exception { + runWithShellPermissionIdentity(() -> { + sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, true); + boolean isRoleVisibleForTest = + sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME); + assertThat(isRoleVisibleForTest).isEqualTo(true); + }); + } + + @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + public void setAndGetIsRoleVisibleForTestSetFalse() throws Exception { + runWithShellPermissionIdentity(() -> { + sRoleManager.setRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME, false); + boolean isRoleVisibleForTest = + sRoleManager.isRoleVisibleForTest(PROFILE_GROUP_EXCLUSIVE_ROLE_NAME); + assertThat(isRoleVisibleForTest).isEqualTo(false); + }); + } + @NonNull private List<String> getRoleHolders(@NonNull String roleName) throws Exception { return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName)); diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt b/tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt new file mode 100644 index 000000000..10a3834a2 --- /dev/null +++ b/tests/cts/role/src/android/app/role/cts/RoleManagerUtil.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.role.cts + +import android.content.res.Configuration +import android.content.res.Resources +import android.util.Log + +object RoleManagerUtil { + private val TAG = RoleManagerUtil::class.java.getSimpleName() + + /** + * This method checks for the minimum screen size described in CDD {@see + * https://source.android.com/docs/compatibility/14/android-14-cdd#7111_screen_size_and_shape} + */ + fun isCddCompliantScreenSize(): Boolean { + if ( + Resources.getSystem().configuration.uiMode and Configuration.UI_MODE_TYPE_MASK == + Configuration.UI_MODE_TYPE_WATCH + ) { + Log.d(TAG, "UI mode is UI_MODE_TYPE_WATCH, skipping the min dp check") + return true + } + + val screenSize = + Resources.getSystem().configuration.screenLayout and + Configuration.SCREENLAYOUT_SIZE_MASK + return when (screenSize) { + Configuration.SCREENLAYOUT_SIZE_SMALL -> hasMinScreenSize(426, 320) + Configuration.SCREENLAYOUT_SIZE_NORMAL -> hasMinScreenSize(480, 320) + Configuration.SCREENLAYOUT_SIZE_LARGE -> hasMinScreenSize(640, 480) + Configuration.SCREENLAYOUT_SIZE_XLARGE -> hasMinScreenSize(960, 720) + else -> { + Log.e(TAG, "Unknown screen size: $screenSize") + true + } + } + } + + private fun hasMinScreenSize(minWidthDp: Int, minHeightDp: Int): Boolean { + val dpi = Resources.getSystem().displayMetrics.densityDpi + val widthDp = (160f / dpi) * Resources.getSystem().displayMetrics.widthPixels + val heightDp = (160f / dpi) * Resources.getSystem().displayMetrics.heightPixels + + // CDD does seem to follow width & height convention correctly, hence checking both ways + return (widthDp >= minWidthDp && heightDp >= minHeightDp) || + (widthDp >= minHeightDp && heightDp >= minWidthDp) + } +} diff --git a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt index 7e58e1848..c9dc97b8f 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt +++ b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt @@ -19,15 +19,19 @@ package android.app.role.cts import android.app.role.RoleManager import android.os.Build import android.os.UserHandle +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.InstrumentationRegistry import androidx.test.filters.SdkSuppress import androidx.test.runner.AndroidJUnit4 import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow +import com.android.permission.flags.Flags import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Assert.assertThrows import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -43,8 +47,23 @@ class RoleShellCommandTest { private var roleHolder: String? = null private var wasBypassingRoleQualification: Boolean = false + @get:Rule val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before - fun saveRoleHolder() { + public fun setUp() { + saveRoleHolder() + saveBypassingRoleQualification() + installApp() + } + + @After + public fun tearDown() { + uninstallApp() + restoreBypassingRoleQualification() + restoreRoleHolder() + } + + private fun saveRoleHolder() { roleHolder = getRoleHolders().firstOrNull() if (roleHolder == APP_PACKAGE_NAME) { removeRoleHolder() @@ -52,31 +71,30 @@ class RoleShellCommandTest { } } - @Before - fun saveBypassingRoleQualification() { + private fun saveBypassingRoleQualification() { wasBypassingRoleQualification = isBypassingRoleQualification() } - @After - fun restoreRoleHolder() { + private fun restoreRoleHolder() { removeRoleHolder() roleHolder?.let { addRoleHolder(it) } assertIsRoleHolder(false) } - @After - fun restoreBypassingRoleQualification() { + private fun restoreBypassingRoleQualification() { setBypassingRoleQualification(wasBypassingRoleQualification) } - @Before - fun installApp() { + private fun installApp() { installPackage(APP_APK_PATH) + // Install CtsRoleTestAppClone as default role holder for browser role + // in case no browser is installed on system + installPackage(APP_CLONE_APK_PATH) } - @After - fun uninstallApp() { + private fun uninstallApp() { uninstallPackage(APP_PACKAGE_NAME) + uninstallPackage(APP_CLONE_PACKAGE_NAME) } @Test @@ -144,6 +162,31 @@ class RoleShellCommandTest { assertThat(isBypassingRoleQualification()).isFalse() } + @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + fun setActiveUserForProfileGroupExclusiveRoleAsUser() { + val activeUser = userId + setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, activeUser) + + val currentActiveUserId = getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + assertThat(currentActiveUserId).isEqualTo(activeUser) + } + + @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + fun setActiveUserForNonProfileGroupExclusiveRoleThenFails() { + assertThrows(AssertionError::class.java) { setActiveUserForRole(ROLE_NAME, userId) } + } + + @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") + @Test + fun getActiveUserForNonProfileGroupExclusiveRoleThenFails() { + assertThrows(AssertionError::class.java) { getActiveUserForRole(ROLE_NAME) } + } + private fun addRoleHolder(packageName: String = APP_PACKAGE_NAME) { runShellCommandOrThrow("cmd role add-role-holder --user $userId $ROLE_NAME $packageName") } @@ -192,9 +235,25 @@ class RoleShellCommandTest { callWithShellPermissionIdentity { roleManager.setBypassingRoleQualification(value) } } + private fun getActiveUserForRole(roleName: String): Int? { + return runShellCommandOrThrow("cmd role get-active-user-for-role --user $userId $roleName") + .trim() + .toIntOrNull() + } + + private fun setActiveUserForRole(roleName: String, activeUserId: Int) { + runShellCommandOrThrow( + "cmd role set-active-user-for-role --user $userId $roleName $activeUserId" + ) + } + companion object { private const val ROLE_NAME = RoleManager.ROLE_BROWSER + private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME = + RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY private const val APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk" private const val APP_PACKAGE_NAME = "android.app.role.cts.app" + private const val APP_CLONE_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestAppClone.apk" + private const val APP_CLONE_PACKAGE_NAME = "android.app.role.cts.appClone" } } diff --git a/tests/cts/rolemultiuser/Android.bp b/tests/cts/rolemultiuser/Android.bp new file mode 100644 index 000000000..7de55fc1b --- /dev/null +++ b/tests/cts/rolemultiuser/Android.bp @@ -0,0 +1,50 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test { + name: "CtsRoleMultiUserTestCases", + defaults: ["mts-target-sdk-version-current"], + sdk_version: "test_current", + min_sdk_version: "30", + + srcs: [ + "src/**/*.kt", + ], + + static_libs: [ + "bedstead-flags", + "bedstead-multiuser", + "com.android.permission.flags-aconfig-java-export", + "ctstestrunner-axt", + "Harrier", + "flag-junit", + "Nene", + "truth", + ], + + test_suites: [ + "cts", + "general-tests", + "mts-permission", + "mcts-permission", + ], + + data: [ + ":CtsRoleMultiUserTestApp", + ], +} diff --git a/tests/cts/rolemultiuser/AndroidManifest.xml b/tests/cts/rolemultiuser/AndroidManifest.xml new file mode 100644 index 000000000..1524c5703 --- /dev/null +++ b/tests/cts/rolemultiuser/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="android.app.rolemultiuser.cts"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.app.rolemultiuser.cts" + android:label="CTS multi-user tests of android.app.role"> + </instrumentation> +</manifest> diff --git a/tests/cts/rolemultiuser/AndroidTest.xml b/tests/cts/rolemultiuser/AndroidTest.xml new file mode 100644 index 000000000..15c34f54a --- /dev/null +++ b/tests/cts/rolemultiuser/AndroidTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Config for CTS role multi-user test cases"> + + <option name="test-suite-tag" value="cts" /> + <option name="config-descriptor:metadata" key="component" value="permissions" /> + <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="multiuser" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" /> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="CtsRoleMultiUserTestCases.apk" /> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="mkdir -p /data/local/tmp/cts-role" /> + <option name="teardown-command" value="rm -rf /data/local/tmp/cts-role"/> + </target_preparer> + + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="CtsRoleMultiUserTestApp.apk->/data/local/tmp/cts-role/CtsRoleMultiUserTestApp.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.app.rolemultiuser.cts" /> + <option name="exclude-annotation" value="com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile" /> + <option name="exclude-annotation" value="com.android.bedstead.multiuser.annotations.RequireRunOnSecondaryUser" /> + <option name="runtime-hint" value="5m" /> + </test> +</configuration> diff --git a/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/Android.bp b/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/Android.bp new file mode 100644 index 000000000..71ccd0e59 --- /dev/null +++ b/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/Android.bp @@ -0,0 +1,23 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CtsRoleMultiUserTestApp", + defaults: ["mts-target-sdk-version-current"], + min_sdk_version: "30", +} diff --git a/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/AndroidManifest.xml b/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/AndroidManifest.xml new file mode 100644 index 000000000..eea3be741 --- /dev/null +++ b/tests/cts/rolemultiuser/CtsRoleMultiUserTestApp/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="android.app.rolemultiuser.cts.app"> + + <application android:label="CtsRoleMultiUserTestApp" /> +</manifest> diff --git a/tests/cts/rolemultiuser/OWNERS b/tests/cts/rolemultiuser/OWNERS new file mode 100644 index 000000000..fb6099cf7 --- /dev/null +++ b/tests/cts/rolemultiuser/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 137825 + +include platform/frameworks/base:/core/java/android/permission/OWNERS diff --git a/tests/cts/rolemultiuser/TEST_MAPPING b/tests/cts/rolemultiuser/TEST_MAPPING new file mode 100644 index 000000000..323e3094c --- /dev/null +++ b/tests/cts/rolemultiuser/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "postsubmit": [ + { + "name": "CtsRoleMultiUserTestCases" + } + ], + "mainline-postsubmit": [ + { + "name": "CtsRoleMultiUserTestCases[com.google.android.permission.apex]" + } + ] +} diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt new file mode 100644 index 000000000..134f45131 --- /dev/null +++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.rolemultiuser.cts + +import android.app.role.RoleManager +import android.content.Context +import android.os.Build +import android.os.Process +import android.os.UserHandle +import androidx.test.filters.SdkSuppress +import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile +import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile +import com.android.bedstead.enterprise.workProfile +import com.android.bedstead.flags.annotations.RequireFlagsEnabled +import com.android.bedstead.harrier.BedsteadJUnit4 +import com.android.bedstead.harrier.DeviceState +import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser +import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile +import com.android.bedstead.multiuser.annotations.EnsureHasSecondaryUser +import com.android.bedstead.multiuser.annotations.RequireRunNotOnSecondaryUser +import com.android.bedstead.multiuser.annotations.RequireRunOnPrimaryUser +import com.android.bedstead.multiuser.privateProfile +import com.android.bedstead.multiuser.secondaryUser +import com.android.bedstead.nene.TestApis.context +import com.android.bedstead.nene.TestApis.permissions +import com.android.bedstead.nene.TestApis.users +import com.android.bedstead.nene.types.OptionalBoolean +import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL +import com.android.bedstead.permissions.CommonPermissions.MANAGE_DEFAULT_APPLICATIONS +import com.android.bedstead.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS +import com.android.bedstead.permissions.annotations.EnsureDoesNotHavePermission +import com.android.bedstead.permissions.annotations.EnsureHasPermission +import com.android.compatibility.common.util.SystemUtil +import com.android.compatibility.common.util.SystemUtil.eventually +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import java.util.Objects +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit +import java.util.function.Consumer +import org.junit.After +import org.junit.Assert.assertThrows +import org.junit.Assume.assumeFalse +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") +@RunWith(BedsteadJUnit4::class) +class RoleManagerMultiUserTest { + @Before + @Throws(java.lang.Exception::class) + fun setUp() { + installAppForAllUsers() + } + + @After + @Throws(java.lang.Exception::class) + fun tearDown() { + uninstallAppForAllUsers() + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @RequireRunOnPrimaryUser + @Test + @Throws(Exception::class) + fun isAvailableAsUserForProfileGroupExclusiveRole() { + val workProfileRoleManager = getRoleManagerForUser(deviceState.workProfile().userHandle()) + val privateProfileRoleManager = + getRoleManagerForUser(deviceState.privateProfile().userHandle()) + + assertThat(roleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)).isTrue() + assertThat(workProfileRoleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isTrue() + assertThat(privateProfileRoleManager.isRoleAvailable(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isFalse() + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @Test + @Throws(Exception::class) + fun cannotGetActiveUserForNonCrossUserRole() { + assertThrows(IllegalArgumentException::class.java) { + roleManager.getActiveUserForRole(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(MANAGE_ROLE_HOLDERS) + @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL) + @Test + @Throws(Exception::class) + fun cannotGetActiveUserForRoleWithoutInteractAcrossUserPermission() { + assertThrows(SecurityException::class.java) { + roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL) + @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS) + @Test + @Throws(Exception::class) + fun cannotGetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() { + assertThrows(SecurityException::class.java) { + roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForNonCrossUserRole() { + assertThrows(IllegalArgumentException::class.java) { + roleManager.setActiveUserForRole( + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, + Process.myUserHandle(), + 0, + ) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(MANAGE_ROLE_HOLDERS) + @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL) + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForRoleWithoutInteractAcrossUserPermission() { + assertThrows(SecurityException::class.java) { + roleManager.setActiveUserForRole( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + Process.myUserHandle(), + 0, + ) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL) + @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS) + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() { + assertThrows(SecurityException::class.java) { + roleManager.setActiveUserForRole( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + Process.myUserHandle(), + 0, + ) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForRoleToNonExistentUser() { + val targetActiveUser = users().nonExisting().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isNotEqualTo(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForRoleToPrivateProfileUser() { + val targetActiveUser = deviceState.privateProfile().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isNotEqualTo(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE) + @EnsureHasSecondaryUser + @RequireRunNotOnSecondaryUser + @Test + @Throws(Exception::class) + fun cannotSetActiveUserForRoleToUserNotInProfileGroup() { + val targetActiveUser = deviceState.secondaryUser().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isNotEqualTo(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE) + @EnsureHasSecondaryUser + @RequireRunNotOnSecondaryUser + @Test + @Throws(java.lang.Exception::class) + fun ensureRoleHasActiveUser() { + val primaryUser = deviceState.initialUser().userHandle() + val primaryUserRoleManager = getRoleManagerForUser(primaryUser) + val secondaryUser = deviceState.secondaryUser().userHandle() + val secondaryUserRoleManager = getRoleManagerForUser(secondaryUser) + + assertWithMessage( + "Expected active user in profile group for user ${primaryUser.identifier}" + ) + .that(primaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isNotNull() + assertWithMessage( + "Expected active user in profile group for user ${secondaryUser.identifier}" + ) + .that( + secondaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isNotNull() + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun ensureOnlyActiveUserIsRoleHolder() { + try { + // Set test default role holder. Ensures fallbacks to a default holder + roleManager.setDefaultHoldersForTest( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + listOf(APP_PACKAGE_NAME), + ) + + val activeUser = roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)!! + // Test app install might take a moment + eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(activeUser) } + } finally { + // Clear test default role holder + roleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, null) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureDoesNotHavePermission(MANAGE_DEFAULT_APPLICATIONS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) + fun setAndGetActiveUserForRoleSetCurrentUserWithManageRoleHoldersPermission() { + assumeFalse( + "setActiveUser not supported for private profile", + users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME, + ) + + val targetActiveUser = users().current().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS) + @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) + fun setAndGetActiveUserForRoleSetCurrentUserWithManageDefaultApplicationPermission() { + assumeFalse( + "setActiveUser not supported for private profile", + users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME, + ) + + val targetActiveUser = users().current().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) + fun setAndGetActiveUserForRoleSetCurrentUserEnsureRoleNotHeldByInactiveUser() { + assumeFalse( + "setActiveUser not supported for private profile", + users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME, + ) + // initialUser needs to be not the targetUser + val targetActiveUser = users().current().userHandle() + val initialUser = + if (Objects.equals(targetActiveUser, deviceState.initialUser())) { + deviceState.workProfile().userHandle() + } else { + deviceState.initialUser().userHandle() + } + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + + try { + // Set test default role holder. Ensures fallbacks to a default holder + roleManager.setDefaultHoldersForTest( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + listOf(APP_PACKAGE_NAME), + ) + + roleManager.setActiveUserForRole( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + targetActiveUser, + 0, + ) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + // We can assume targetActiveUser is role holder since fallback is enabled + eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) } + } finally { + // Clear test default role holder + roleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, null) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) + fun setAndGetActiveUserForRoleSetWorkProfile() { + try { + // Set test default role holder. Ensures fallbacks to a default holder + roleManager.setDefaultHoldersForTest( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + listOf(APP_PACKAGE_NAME), + ) + + val targetActiveUser = deviceState.workProfile().userHandle() + roleManager.setActiveUserForRole( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + targetActiveUser, + 0, + ) + + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + // We can assume targetActiveUser is role holder since fallback is enabled + eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) } + } finally { + // Clear test default role holder + roleManager.setDefaultHoldersForTest(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, null) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(MANAGE_ROLE_HOLDERS) + @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL) + @EnsureHasWorkProfile + @RequireRunOnPrimaryUser + @Test + @Throws(Exception::class) + fun cannotAddRoleHolderAsUserForProfileExclusiveRoleWithoutInteractAcrossUserPermission() { + // Set other user as active + val initialUser = deviceState.workProfile().userHandle() + // setActiveUserForRole and getActiveUserForRole is used to ensure initial active users + // state and requires INTERACT_ACROSS_USERS_FULL + permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ -> + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + } + + val targetActiveUser = users().current().userHandle() + val future = CallbackFuture() + assertThrows(SecurityException::class.java) { + roleManager.addRoleHolderAsUser( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + targetActiveUser, + context.mainExecutor, + future, + ) + } + assertThat( + roleManager.getRoleHoldersAsUser( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + targetActiveUser, + ) + ) + .isEmpty() + + // getActiveUserForRole is used to ensure addRoleHolderAsUser didn't set active user, and + // requires INTERACT_ACROSS_USERS_FULL + permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ -> + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun addRoleHolderAsUserSetsCurrentUserAsActive() { + // Set other user as active + val initialUser = deviceState.workProfile().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + + val targetActiveUser = users().current().userHandle() + val future = CallbackFuture() + roleManager.addRoleHolderAsUser( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + targetActiveUser, + context.mainExecutor, + future, + ) + assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @RequireRunOnWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun addRoleHolderAsUserSetsWorkProfileAsActive() { + // Set other user as active + val initialUser = users().main()!!.userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + + val targetActiveUser = deviceState.workProfile().userHandle() + + assertThat(targetActiveUser).isNotEqualTo(initialUser) + val future = CallbackFuture() + roleManager.addRoleHolderAsUser( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + targetActiveUser, + context.mainExecutor, + future, + ) + assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(MANAGE_DEFAULT_APPLICATIONS) + @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL) + @EnsureHasWorkProfile + @RequireRunOnPrimaryUser + @Test + @Throws(Exception::class) + fun cannotSetDefaultApplicationForProfileExclusiveRoleWithoutInteractAcrossUserPermission() { + // Set other user as active + val initialUser = deviceState.workProfile().userHandle() + // setActiveUserForRole and getActiveUserForRole is used to ensure initial active users + // state and requires INTERACT_ACROSS_USERS_FULL + permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ -> + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + } + + val future = CallbackFuture() + assertThrows(SecurityException::class.java) { + roleManager.setDefaultApplication( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + context.mainExecutor, + future, + ) + } + assertThat(roleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)).isNull() + + // getActiveUserForRole is used to ensure setDefaultApplication didn't set active user, + // and requires INTERACT_ACROSS_USERS_FULL + permissions().withPermission(INTERACT_ACROSS_USERS_FULL).use { _ -> + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS) + @EnsureHasWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun setDefaultApplicationSetsCurrentUserAsActive() { + // Set other user as active + val initialUser = deviceState.workProfile().userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + + val targetActiveUser = users().current().userHandle() + val future = CallbackFuture() + roleManager.setDefaultApplication( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + context.mainExecutor, + future, + ) + assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS) + @EnsureHasWorkProfile + @RequireRunOnWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun setDefaultApplicationSetsWorkProfileAsActive() { + // Set other user as active + val initialUser = users().main()!!.userHandle() + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(initialUser) + + val targetActiveUser = deviceState.workProfile().userHandle() + assertThat(targetActiveUser).isNotEqualTo(initialUser) + val future = CallbackFuture() + roleManager.setDefaultApplication( + PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, + APP_PACKAGE_NAME, + 0, + context.mainExecutor, + future, + ) + assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) + } + + @Throws(java.lang.Exception::class) + private fun installAppForAllUsers() { + SystemUtil.runShellCommandOrThrow("pm install -r --user all $APP_APK_PATH") + } + + private fun uninstallAppForAllUsers() { + SystemUtil.runShellCommand("pm uninstall $APP_PACKAGE_NAME") + } + + private fun assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser( + expectedActiveUser: UserHandle + ) { + for (userReference in users().profileGroup()) { + val user = userReference.userHandle() + if (Objects.equals(user, expectedActiveUser)) { + val roleHolders = + roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + assertWithMessage( + "Expected user ${user.identifier} to have a role holder for " + + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that(roleHolders) + .isNotEmpty() + assertWithMessage( + "Expected user ${user.identifier} to have $APP_PACKAGE_NAME as role " + + "holder for $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that(roleHolders.first()) + .isEqualTo(APP_PACKAGE_NAME) + } else { + // Verify the non-active user does not hold the role + assertWithMessage( + "Expected user ${user.identifier} to not have a role holder for" + + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that( + roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + ) + .isEmpty() + } + } + } + + private fun assertExpectedProfileHasRoleUsingGetDefaultApplication( + expectedActiveUser: UserHandle + ) { + for (userReference in users().profileGroup()) { + val user = userReference.userHandle() + val userRoleManager = getRoleManagerForUser(user) + if (Objects.equals(user, expectedActiveUser)) { + assertWithMessage("Expected default application for user ${user.identifier}") + .that( + userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isEqualTo(APP_PACKAGE_NAME) + } else { + // Verify the non-active user does not hold the role + assertWithMessage("Expected no default application for user ${user.identifier}") + .that( + userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isNull() + } + } + } + + private fun getRoleManagerForUser(user: UserHandle): RoleManager { + val userContext = context.createContextAsUser(user, 0) + return userContext.getSystemService(RoleManager::class.java) + } + + class CallbackFuture : CompletableFuture<Boolean?>(), Consumer<Boolean?> { + override fun accept(successful: Boolean?) { + complete(successful) + } + } + + companion object { + private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong() + private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME = + RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY + private const val PRIVATE_PROFILE_TYPE_NAME = "android.os.usertype.profile.PRIVATE" + private const val APP_APK_PATH: String = + "/data/local/tmp/cts-role/CtsRoleMultiUserTestApp.apk" + private const val APP_PACKAGE_NAME: String = "android.app.rolemultiuser.cts.app" + private val context: Context = context().instrumentedContext() + private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java) + + @JvmField @ClassRule @Rule val deviceState = DeviceState() + } +} diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt index c344d7ebd..1320c2ff9 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt @@ -492,8 +492,8 @@ class SafetyCenterDataTest { status1, listOf(issue1), listOf(entryOrGroup1), - listOf(staticEntryGroup1) - ) + listOf(staticEntryGroup1), + ), ) .addEqualityGroup( data2, @@ -501,19 +501,19 @@ class SafetyCenterDataTest { status2, listOf(issue2), listOf(entryOrGroup2), - listOf(staticEntryGroup2) - ) + listOf(staticEntryGroup2), + ), ) .addEqualityGroup( SafetyCenterData(status1, listOf(), listOf(), listOf()), - SafetyCenterData(status1, listOf(), listOf(), listOf()) + SafetyCenterData(status1, listOf(), listOf(), listOf()), ) .addEqualityGroup( SafetyCenterData( status2, listOf(issue1), listOf(entryOrGroup1), - listOf(staticEntryGroup1) + listOf(staticEntryGroup1), ) ) .addEqualityGroup( @@ -521,7 +521,7 @@ class SafetyCenterDataTest { status1, listOf(issue2), listOf(entryOrGroup1), - listOf(staticEntryGroup1) + listOf(staticEntryGroup1), ) ) .addEqualityGroup( @@ -529,7 +529,7 @@ class SafetyCenterDataTest { status1, listOf(issue1), listOf(entryOrGroup2), - listOf(staticEntryGroup1) + listOf(staticEntryGroup1), ) ) .addEqualityGroup( @@ -537,7 +537,7 @@ class SafetyCenterDataTest { status1, listOf(issue1), listOf(entryOrGroup1), - listOf(staticEntryGroup2) + listOf(staticEntryGroup2), ) ) @@ -550,7 +550,7 @@ class SafetyCenterDataTest { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterData.CREATOR, ignoreToString = true, - createCopy = { SafetyCenterData.Builder(it).build() } + createCopy = { SafetyCenterData.Builder(it).build() }, ) .addEqualityGroup( data1, @@ -558,7 +558,7 @@ class SafetyCenterDataTest { status1, listOf(issue1), listOf(entryOrGroup1), - listOf(staticEntryGroup1) + listOf(staticEntryGroup1), ), SafetyCenterData.Builder(status1) .addIssue(issue1) @@ -570,7 +570,7 @@ class SafetyCenterDataTest { .addEntryOrGroup(entryOrGroup1) .addStaticEntryGroup(staticEntryGroup1) .setExtras(unknownExtras) - .build() + .build(), ) .addEqualityGroup( SafetyCenterData.Builder(status1) @@ -640,7 +640,7 @@ class SafetyCenterDataTest { .addStaticEntryGroup(staticEntryGroup1) .addIssue(issue1) .setExtras(filledExtrasIssuesToGroups1) - .build() + .build(), ) .addEqualityGroup( SafetyCenterData.Builder(status1) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt index 7ae5fb347..38ed449f7 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt @@ -175,7 +175,7 @@ class SafetyCenterEntryGroupTest { fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterEntryGroup.CREATOR, - createCopy = { SafetyCenterEntryGroup.Builder(it).build() } + createCopy = { SafetyCenterEntryGroup.Builder(it).build() }, ) .addEqualityGroup( entryGroup1, @@ -183,7 +183,7 @@ class SafetyCenterEntryGroupTest { .setSummary("A group summary") .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK) .setEntries(listOf(entry1)) - .build() + .build(), ) .addEqualityGroup(entryGroup2) .addEqualityGroup( diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt index 116164288..2811b87f2 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt @@ -41,18 +41,18 @@ class SafetyCenterEntryTest { context, 0, Intent("Fake Different Data"), - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) private val iconAction1 = SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent1 + pendingIntent1, ) private val iconAction2 = SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent2 + pendingIntent2, ) private val entry1 = @@ -213,7 +213,7 @@ class SafetyCenterEntryTest { fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterEntry.CREATOR, - createCopy = { SafetyCenterEntry.Builder(it).build() } + createCopy = { SafetyCenterEntry.Builder(it).build() }, ) .addEqualityGroup(entry1) .addEqualityGroup( @@ -226,7 +226,7 @@ class SafetyCenterEntryTest { .setPendingIntent(pendingIntent1) .setIconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent2 + pendingIntent2, ) .build(), SafetyCenterEntry.Builder("id", "a title") @@ -238,9 +238,9 @@ class SafetyCenterEntryTest { .setPendingIntent(pendingIntent1) .setIconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent2 + pendingIntent2, ) - .build() + .build(), ) .addEqualityGroup(SafetyCenterEntry.Builder(entry1).setId("a different id").build()) .addEqualityGroup( @@ -274,7 +274,7 @@ class SafetyCenterEntryTest { assertThat( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent1 + pendingIntent1, ) .type ) @@ -282,7 +282,7 @@ class SafetyCenterEntryTest { assertThat( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent1 + pendingIntent1, ) .type ) @@ -294,7 +294,7 @@ class SafetyCenterEntryTest { assertThat( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent1 + pendingIntent1, ) .pendingIntent ) @@ -302,7 +302,7 @@ class SafetyCenterEntryTest { assertThat( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent2 + pendingIntent2, ) .pendingIntent ) @@ -340,26 +340,26 @@ class SafetyCenterEntryTest { iconAction1, SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent1 - ) + pendingIntent1, + ), ) .addEqualityGroup( iconAction2, SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent2 - ) + pendingIntent2, + ), ) .addEqualityGroup( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, - pendingIntent1 + pendingIntent1, ) ) .addEqualityGroup( SafetyCenterEntry.IconAction( SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, - pendingIntent2 + pendingIntent2, ) ) .test() diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt index 0d97026bd..e7565bf61 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt @@ -58,7 +58,7 @@ class SafetyCenterErrorDetailsTest { .addEqualityGroup(errorDetails2, SafetyCenterErrorDetails("another error message")) .addEqualityGroup( SafetyCenterErrorDetails("a different error message"), - SafetyCenterErrorDetails("a different error message") + SafetyCenterErrorDetails("a different error message"), ) .test() } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt index be7ca343c..fd359f600 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt @@ -47,7 +47,7 @@ class SafetyCenterIssueTest { context, 0, Intent("Fake Different Data"), - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) private val action1 = @@ -451,7 +451,7 @@ class SafetyCenterIssueTest { .setIsInFlight(true) .setSuccessMessage("a success message") .setConfirmationDialogDetails(confirmationDialogDetails) - .build() + .build(), ) .addEqualityGroup( SafetyCenterIssue.Action.Builder("an_id", "a label", pendingIntent1) @@ -534,7 +534,7 @@ class SafetyCenterIssueTest { .build(), SafetyCenterIssue.Builder(issueWithTiramisuFields) .setAttributionTitle("Attribution title") - .build() + .build(), ) .addEqualityGroup( SafetyCenterIssue.Builder(issueWithTiramisuFields) @@ -549,7 +549,7 @@ class SafetyCenterIssueTest { ) .addEqualityGroup( SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(), - SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build() + SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(), ) .addEqualityGroup( SafetyCenterIssue.Builder(issueWithTiramisuFields) @@ -625,7 +625,7 @@ class SafetyCenterIssueTest { ) .addEqualityGroup( ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"), - ConfirmationDialogDetails("Title", "Text", "Accept", "Deny") + ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"), ) .addEqualityGroup(ConfirmationDialogDetails("Other title", "Text", "Accept", "Deny")) .addEqualityGroup(ConfirmationDialogDetails("Title", "Other text", "Accept", "Deny")) @@ -643,7 +643,7 @@ class SafetyCenterIssueTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterIssue.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build()) .addEqualityGroup(issueWithRequiredFieldsOnly) @@ -657,7 +657,7 @@ class SafetyCenterIssueTest { .setSubtitle("In the neighborhood") .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK) .setActions(listOf(action1)) - .build() + .build(), ) .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build()) .addEqualityGroup( @@ -685,7 +685,7 @@ class SafetyCenterIssueTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterIssue.Action.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup(action1) .addEqualityGroup(action2) @@ -699,7 +699,7 @@ class SafetyCenterIssueTest { .setWillResolve(true) .setIsInFlight(true) .setSuccessMessage("a success message") - .build() + .build(), ) .addEqualityGroup( SafetyCenterIssue.Action.Builder("an_id", "a label", pendingIntent1) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt index 298d7643c..b1c731f08 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt @@ -95,6 +95,7 @@ import com.google.common.util.concurrent.MoreExecutors.directExecutor import kotlin.test.assertFails import kotlin.test.assertFailsWith import kotlinx.coroutines.TimeoutCancellationException +import org.junit.Assume.assumeFalse import org.junit.Assume.assumeTrue import org.junit.Rule import org.junit.Test @@ -215,7 +216,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( DYNAMIC_OTHER_PACKAGE_ID, - safetySourceTestData.unspecified + safetySourceTestData.unspecified, ) } @@ -280,7 +281,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( DYNAMIC_IN_STATELESS_ID, - safetySourceTestData.information + safetySourceTestData.information, ) } @@ -341,7 +342,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( ISSUE_ONLY_BAREBONE_ID, - safetySourceTestData.unspecified + safetySourceTestData.unspecified, ) } @@ -358,7 +359,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( DYNAMIC_BAREBONE_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) } @@ -399,7 +400,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithIssue + safetySourceTestData.informationWithIssue, ) } @@ -420,7 +421,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) } @@ -453,7 +454,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( DYNAMIC_ALL_OPTIONAL_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) } @@ -489,7 +490,7 @@ class SafetyCenterManagerTest { ISSUE_ONLY_ALL_OPTIONAL_ID, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalResolvingGeneralIssue - ) + ), ) } @@ -520,7 +521,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.issueCategoryAllowlists = mapOf( ISSUE_CATEGORY_DEVICE to setOf(SAMPLE_SOURCE_ID), - ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID) + ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID), ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) @@ -538,7 +539,7 @@ class SafetyCenterManagerTest { mapOf( ISSUE_CATEGORY_ACCOUNT to setOf(SINGLE_SOURCE_ID, SAMPLE_SOURCE_ID), ISSUE_CATEGORY_DEVICE to setOf(SAMPLE_SOURCE_ID), - ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID) + ISSUE_CATEGORY_GENERAL to setOf(SAMPLE_SOURCE_ID), ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) @@ -559,7 +560,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) } @@ -576,7 +577,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.issueCategoryAllowlists = mapOf( ISSUE_CATEGORY_ACCOUNT to setOf(SAMPLE_SOURCE_ID), - ISSUE_CATEGORY_DEVICE to setOf(SINGLE_SOURCE_ID) + ISSUE_CATEGORY_DEVICE to setOf(SINGLE_SOURCE_ID), ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) @@ -584,7 +585,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) } @@ -604,7 +605,7 @@ class SafetyCenterManagerTest { safetyCenterManager.setSafetySourceDataWithPermission( SINGLE_SOURCE_ID, safetySourceTestData.unspecified, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) safetyCenterTestHelper.setEnabled(true) @@ -619,7 +620,7 @@ class SafetyCenterManagerTest { safetyCenterManager.setSafetySourceData( SINGLE_SOURCE_ID, safetySourceTestData.unspecified, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) } } @@ -701,7 +702,7 @@ class SafetyCenterManagerTest { safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) listener.receiveSafetyCenterData() @@ -716,7 +717,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) } @@ -731,7 +732,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterManager.reportSafetySourceErrorWithPermission( STATIC_BAREBONE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) } @@ -748,7 +749,7 @@ class SafetyCenterManagerTest { assertFailsWith(IllegalArgumentException::class) { safetyCenterManager.reportSafetySourceErrorWithPermission( DYNAMIC_OTHER_PACKAGE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) } @@ -768,7 +769,7 @@ class SafetyCenterManagerTest { safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) assertFailsWith(TimeoutCancellationException::class) { @@ -781,13 +782,17 @@ class SafetyCenterManagerTest { assertFailsWith(SecurityException::class) { safetyCenterManager.reportSafetySourceError( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) } } @Test fun safetyCenterEnabledChanged_whenImplicitReceiverHasPermission_receiverCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) // Implicit broadcast is only sent to system user. assumeTrue(context.getSystemService(UserManager::class.java)!!.isSystemUser) val enabledChangedReceiver = SafetyCenterEnabledChangedReceiver(context) @@ -806,6 +811,10 @@ class SafetyCenterManagerTest { @Test fun safetyCenterEnabledChanged_whenImplicitReceiverDoesntHavePermission_receiverNotCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) // Implicit broadcast is only sent to system user. assumeTrue(context.getSystemService(UserManager::class.java)!!.isSystemUser) val enabledChangedReceiver = SafetyCenterEnabledChangedReceiver(context) @@ -818,6 +827,10 @@ class SafetyCenterManagerTest { @Test fun safetyCenterEnabledChanged_whenSourceReceiverHasPermission_receiverCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) val receiverValue = @@ -833,18 +846,26 @@ class SafetyCenterManagerTest { @Test fun safetyCenterEnabledChanged_valueDoesntChange_receiverNotCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) assertFailsWith(TimeoutCancellationException::class) { SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait( true, - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } } @Test fun safetyCenterEnabledChanged_whenSourceReceiverDoesntHavePermission_receiverNotCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) assertFailsWith(TimeoutCancellationException::class) { @@ -854,10 +875,30 @@ class SafetyCenterManagerTest { @Test fun safetyCenterEnabledChanged_whenSourceReceiverNotInConfig_receiverNotCalled() { + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) + assertFailsWith(TimeoutCancellationException::class) { + SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait( + false, + TIMEOUT_SHORT, + ) + } + } + + @Test + fun safetyCenterEnabledChanged_whenNoDeviceConfigFlag_receiverNotCalled() { + assumeFalse( + "SafetyCenter DeviceConfig flag is in use", + SafetyCenterTestHelper.safetyCenterCanBeToggledUsingDeviceConfig(), + ) + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + assertFailsWith(TimeoutCancellationException::class) { SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait( false, - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } } @@ -867,7 +908,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_RESCAN_BUTTON_CLICK @@ -884,7 +925,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -902,7 +943,7 @@ class SafetyCenterManagerTest { SafetySourceReceiver.runInForegroundService = true SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -920,13 +961,13 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } @@ -941,7 +982,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -974,11 +1015,11 @@ class SafetyCenterManagerTest { SafetySourceReceiver.apply { setResponse( Request.Rescan(SOURCE_ID_1), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) setResponse( Request.Rescan(SOURCE_ID_3), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) } @@ -1006,11 +1047,11 @@ class SafetyCenterManagerTest { SafetySourceReceiver.apply { setResponse( Request.Refresh(SOURCE_ID_1), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) setResponse( Request.Refresh(SOURCE_ID_3), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) } @@ -1036,7 +1077,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) assertFailsWith(TimeoutCancellationException::class) { @@ -1053,13 +1094,13 @@ class SafetyCenterManagerTest { fun refreshSafetySources_whenSourceNotInConfig_sourceDoesntSendData() { SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } } @@ -1081,7 +1122,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val broadcastId1 = @@ -1102,7 +1143,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information, overrideBroadcastId = "invalid") + Response.SetData(safetySourceTestData.information, overrideBroadcastId = "invalid"), ) val listener = safetyCenterTestHelper.addListener() @@ -1116,7 +1157,7 @@ class SafetyCenterManagerTest { SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN @@ -1131,14 +1172,14 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1160,7 +1201,7 @@ class SafetyCenterManagerTest { ) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1189,7 +1230,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1208,7 +1249,7 @@ class SafetyCenterManagerTest { ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1228,7 +1269,7 @@ class SafetyCenterManagerTest { for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) { SafetySourceReceiver.setResponse( Request.Rescan(sourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) } val listener = safetyCenterTestHelper.addListener() @@ -1248,7 +1289,7 @@ class SafetyCenterManagerTest { for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) { SafetySourceReceiver.setResponse( Request.Rescan(sourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) } val listener = safetyCenterTestHelper.addListener() @@ -1267,7 +1308,7 @@ class SafetyCenterManagerTest { // SOURCE_ID_1 and SOURCE_ID_2 will timeout SafetySourceReceiver.setResponse( Request.Rescan(SOURCE_ID_3), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val listener = safetyCenterTestHelper.addListener() @@ -1314,7 +1355,7 @@ class SafetyCenterManagerTest { assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } } @@ -1336,15 +1377,15 @@ class SafetyCenterManagerTest { SafetySourceReceiver.apply { setResponse( Request.Refresh(SOURCE_ID_1), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) setResponse( Request.Refresh(SOURCE_ID_2), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) setResponse( Request.Refresh(SOURCE_ID_3), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) } // But sources 1 and 3 should not be refreshed in background @@ -1368,7 +1409,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID) @@ -1385,7 +1426,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID) @@ -1403,14 +1444,14 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID) assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PERIODIC, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } @@ -1425,7 +1466,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1443,21 +1484,21 @@ class SafetyCenterManagerTest { SafetySourceReceiver.apply { setResponse( Request.Refresh(SOURCE_ID_1), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) setResponse( Request.Refresh(SOURCE_ID_2), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) setResponse( Request.Refresh(SOURCE_ID_3), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) } safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2) + safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2), ) val apiSafetySourceData1 = @@ -1477,7 +1518,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) assertFailsWith(TimeoutCancellationException::class) { @@ -1500,7 +1541,7 @@ class SafetyCenterManagerTest { assertFails { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3) + safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3), ) } } @@ -1606,7 +1647,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val safetyCenterDataFromListener = listener.receiveSafetyCenterData() @@ -1667,7 +1708,7 @@ class SafetyCenterManagerTest { } safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - oneShotListener + oneShotListener, ) // Check that we don't deadlock when using a one-shot listener. This is because adding the @@ -1684,7 +1725,7 @@ class SafetyCenterManagerTest { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) assertFailsWith(TimeoutCancellationException::class) { @@ -1723,7 +1764,7 @@ class SafetyCenterManagerTest { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( fakeExecutor, - listener + listener, ) fakeExecutor.getNextTask().run() listener.receiveSafetyCenterData() @@ -1771,7 +1812,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationDismissPendingIntentIssue + safetySourceTestData.recommendationDismissPendingIntentIssue, ) val apiSafetySourceDataBeforeDismissal = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) @@ -1781,7 +1822,7 @@ class SafetyCenterManagerTest { ) SafetySourceReceiver.setResponse( Request.DismissIssue(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.dismissSafetyCenterIssueWithPermissionAndWait( @@ -1798,7 +1839,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() @@ -1816,7 +1857,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID) @@ -1837,7 +1878,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID) @@ -1848,7 +1889,7 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - issueTypeId = "some_other_issue_type_id" + issueTypeId = "some_other_issue_type_id", ) ) @@ -1862,7 +1903,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() safetyCenterTestHelper.setEnabled(false) @@ -1890,7 +1931,7 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - userId = USER_NULL + userId = USER_NULL, ) ) } @@ -1908,7 +1949,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val apiSafetySourceDataBeforeExecution = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) @@ -1918,7 +1959,7 @@ class SafetyCenterManagerTest { ) SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -1926,8 +1967,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val apiSafetySourceDataAfterExecution = @@ -1948,8 +1989,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val error = listener.receiveSafetyCenterErrorDetails() @@ -1974,8 +2015,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val error = listener.receiveSafetyCenterErrorDetails() @@ -2002,7 +2043,7 @@ class SafetyCenterManagerTest { } safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( fakeExecutor, - listener + listener, ) fakeExecutor.getNextTask().run() @@ -2011,8 +2052,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) fakeExecutor.getNextTask().run() @@ -2031,9 +2072,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } assertFailsWith(TimeoutCancellationException::class) { @@ -2046,7 +2087,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -2054,8 +2095,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) listener.receiveSafetyCenterData() @@ -2065,9 +2106,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } assertFailsWith(TimeoutCancellationException::class) { @@ -2080,7 +2121,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() safetyCenterTestHelper.setEnabled(false) @@ -2091,9 +2132,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } assertFailsWith(TimeoutCancellationException::class) { @@ -2113,12 +2154,12 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) assertFailsWith(IllegalArgumentException::class) { @@ -2127,9 +2168,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID + "invalid", - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } @@ -2143,12 +2184,12 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) assertFailsWith(TimeoutCancellationException::class) { @@ -2157,9 +2198,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID + "invalid" + CRITICAL_ISSUE_ACTION_ID + "invalid", ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } @@ -2176,8 +2217,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SOURCE_ID_1, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) } } @@ -2189,14 +2230,14 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - userId = USER_NULL + userId = USER_NULL, ), SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, CRITICAL_ISSUE_ACTION_ID, - userId = USER_NULL - ) + userId = USER_NULL, + ), ) } } @@ -2214,7 +2255,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.unspecified) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission() @@ -2230,7 +2271,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setEnabled(false) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt index 71be3d5eb..0709bfc83 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt @@ -104,11 +104,11 @@ class SafetyCenterStaticEntryGroupTest { ) .addEqualityGroup( staticEntryGroup, - SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2)) + SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2)), ) .addEqualityGroup( SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1)), - SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1)) + SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1)), ) .addEqualityGroup( SafetyCenterStaticEntryGroup("a different title", listOf(staticEntry1)) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt index 045a50f75..7a5e6a737 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt @@ -40,7 +40,7 @@ class SafetyCenterStaticEntryTest { context, 0, Intent("Fake Different Data"), - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) private val title1 = "a title" @@ -101,14 +101,14 @@ class SafetyCenterStaticEntryTest { fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterStaticEntry.CREATOR, - createCopy = { SafetyCenterStaticEntry.Builder(it).build() } + createCopy = { SafetyCenterStaticEntry.Builder(it).build() }, ) .addEqualityGroup( staticEntry1, SafetyCenterStaticEntry.Builder("a title") .setSummary("a summary") .setPendingIntent(pendingIntent1) - .build() + .build(), ) .addEqualityGroup(staticEntry2) .addEqualityGroup(staticEntryMinimal, SafetyCenterStaticEntry.Builder("").build()) @@ -120,7 +120,7 @@ class SafetyCenterStaticEntryTest { SafetyCenterStaticEntry.Builder("titlee") .setSummary("sumaree") .setPendingIntent(pendingIntent1) - .build() + .build(), ) .addEqualityGroup( SafetyCenterStaticEntry.Builder(staticEntry1).setTitle("a different title").build() diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt index 4df4e5d35..561e34d3b 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt @@ -156,7 +156,7 @@ class SafetyCenterStatusTest { fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterStatus.CREATOR, - createCopy = { SafetyCenterStatus.Builder(it).build() } + createCopy = { SafetyCenterStatus.Builder(it).build() }, ) .addEqualityGroup( baseStatus, @@ -164,7 +164,7 @@ class SafetyCenterStatusTest { .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION) .setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS) .build(), - SafetyCenterStatus.Builder(baseStatus).build() + SafetyCenterStatus.Builder(baseStatus).build(), ) .addEqualityGroup( SafetyCenterStatus.Builder("same title", "same summary") @@ -172,7 +172,7 @@ class SafetyCenterStatusTest { .build(), SafetyCenterStatus.Builder("same title", "same summary") .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) - .build() + .build(), ) .addEqualityGroup( SafetyCenterStatus.Builder(baseStatus).setTitle("that's not it").build() diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt index d882fc3cb..3beced151 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt @@ -119,7 +119,7 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.setSafetySourceDataWithPermission( SINGLE_SOURCE_ID, safetySourceTestData.criticalWithResolvingGeneralIssue, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) val apiSafetySourceData = @@ -134,7 +134,7 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.setSafetySourceData( SINGLE_SOURCE_ID, safetySourceTestData.unspecified, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) } } @@ -154,12 +154,12 @@ class SafetyCenterUnsupportedTest { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) assertFailsWith(TimeoutCancellationException::class) { @@ -172,7 +172,7 @@ class SafetyCenterUnsupportedTest { assertFailsWith(SecurityException::class) { safetyCenterManager.reportSafetySourceError( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) } } @@ -186,7 +186,7 @@ class SafetyCenterUnsupportedTest { assertFailsWith(TimeoutCancellationException::class) { enabledChangedReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait( false, - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } enabledChangedReceiver.unregister() @@ -201,7 +201,7 @@ class SafetyCenterUnsupportedTest { assertFailsWith(TimeoutCancellationException::class) { SafetySourceReceiver.setSafetyCenterEnabledWithReceiverPermissionAndWait( false, - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } } @@ -215,7 +215,7 @@ class SafetyCenterUnsupportedTest { assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } } @@ -260,7 +260,7 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) assertFailsWith(TimeoutCancellationException::class) { @@ -285,7 +285,7 @@ class SafetyCenterUnsupportedTest { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener) @@ -300,7 +300,7 @@ class SafetyCenterUnsupportedTest { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) assertFailsWith(SecurityException::class) { @@ -316,12 +316,12 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.setSafetySourceDataWithPermission( SINGLE_SOURCE_ID, safetySourceTestData.criticalWithResolvingGeneralIssue, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( @@ -348,12 +348,12 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.setSafetySourceDataWithPermission( SINGLE_SOURCE_ID, safetySourceTestData.criticalWithResolvingGeneralIssue, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) assertFailsWith(TimeoutCancellationException::class) { @@ -362,9 +362,9 @@ class SafetyCenterUnsupportedTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } assertFailsWith(TimeoutCancellationException::class) { @@ -387,7 +387,7 @@ class SafetyCenterUnsupportedTest { safetyCenterManager.setSafetySourceDataWithPermission( SINGLE_SOURCE_ID, safetySourceTestData.criticalWithResolvingGeneralIssue, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) val apiSafetySourceDataBeforeClearing = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt index 4d1cb6a8b..cba29852f 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt @@ -203,7 +203,7 @@ class SafetyEventTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyEvent.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()) .addEqualityGroup( @@ -212,7 +212,7 @@ class SafetyEventTest { .build(), SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) .setRefreshBroadcastId(REFRESH_BROADCAST_ID) - .build() + .build(), ) .addEqualityGroup( SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) @@ -222,7 +222,7 @@ class SafetyEventTest { SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + .build(), ) .addEqualityGroup( SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) @@ -232,7 +232,7 @@ class SafetyEventTest { SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + .build(), ) .addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()) .addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED).build()) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt index ba2ab3d76..287aa15a3 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt @@ -350,11 +350,11 @@ class SafetySourceDataTest { EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = SafetySourceData.CREATOR) .addEqualityGroup( SafetySourceData.Builder().setStatus(firstStatus).build(), - SafetySourceData.Builder().setStatus(firstStatus).build() + SafetySourceData.Builder().setStatus(firstStatus).build(), ) .addEqualityGroup( SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(), - SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build() + SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(), ) .addEqualityGroup( SafetySourceData.Builder() @@ -366,7 +366,7 @@ class SafetySourceDataTest { .setStatus(firstStatus) .addIssue(firstIssue) .addIssue(secondIssue) - .build() + .build(), ) .addEqualityGroup(SafetySourceData.Builder().setStatus(secondStatus).build()) .addEqualityGroup( @@ -394,11 +394,11 @@ class SafetySourceDataTest { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetySourceData.CREATOR, ignoreToString = true, - createCopy = { SafetySourceData.Builder(it).build() } + createCopy = { SafetySourceData.Builder(it).build() }, ) .addEqualityGroup( SafetySourceData.Builder().setStatus(firstStatus).build(), - SafetySourceData.Builder().setStatus(firstStatus).setExtras(filledExtras).build() + SafetySourceData.Builder().setStatus(firstStatus).setExtras(filledExtras).build(), ) .addEqualityGroup( SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(), @@ -406,7 +406,7 @@ class SafetySourceDataTest { .addIssue(firstIssue) .addIssue(secondIssue) .setExtras(filledExtras) - .build() + .build(), ) .addEqualityGroup( SafetySourceData.Builder() @@ -419,11 +419,11 @@ class SafetySourceDataTest { .addIssue(firstIssue) .addIssue(secondIssue) .setExtras(filledExtras) - .build() + .build(), ) .addEqualityGroup( SafetySourceData.Builder().setStatus(secondStatus).build(), - SafetySourceData.Builder().setStatus(secondStatus).setExtras(filledExtras).build() + SafetySourceData.Builder().setStatus(secondStatus).setExtras(filledExtras).build(), ) .addEqualityGroup( SafetySourceData.Builder().addIssue(secondIssue).addIssue(firstIssue).build(), @@ -431,11 +431,11 @@ class SafetySourceDataTest { .addIssue(secondIssue) .addIssue(firstIssue) .setExtras(filledExtras) - .build() + .build(), ) .addEqualityGroup( SafetySourceData.Builder().addIssue(firstIssue).build(), - SafetySourceData.Builder().addIssue(firstIssue).setExtras(filledExtras).build() + SafetySourceData.Builder().addIssue(firstIssue).setExtras(filledExtras).build(), ) .addEqualityGroup( SafetySourceData.Builder() @@ -448,7 +448,7 @@ class SafetySourceDataTest { .addIssue(firstIssue) .addIssue(secondIssue) .setExtras(filledExtras) - .build() + .build(), ) .test() } @@ -485,7 +485,7 @@ class SafetySourceDataTest { context, /* requestCode = */ 0, Intent("Status PendingIntent $id"), - FLAG_IMMUTABLE + FLAG_IMMUTABLE, ) ) .build() @@ -496,7 +496,7 @@ class SafetySourceDataTest { "Issue summary $id", "Issue summary $id", severityLevel, - "Issue type id $id" + "Issue type id $id", ) .addAction( SafetySourceIssue.Action.Builder( @@ -506,8 +506,8 @@ class SafetySourceDataTest { context, /* requestCode = */ 0, Intent("Issue PendingIntent $id"), - FLAG_IMMUTABLE - ) + FLAG_IMMUTABLE, + ), ) .build() ) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt index 336462491..4133f6f17 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt @@ -51,7 +51,7 @@ class SafetySourceErrorDetailsTest { SafetySourceErrorDetails(SAFETY_EVENT), SafetySourceErrorDetails( SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() - ) + ), ) .addEqualityGroup( SafetySourceErrorDetails( diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt index 2d19a3175..a7149e4d7 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt @@ -238,7 +238,7 @@ class SafetySourceIssueTest { Action.Builder("action_id", "Action label", pendingIntent1) .setConfirmationDialogDetails(confirmationDialogDetails) .setWillResolve(false) - .build() + .build(), ) .addEqualityGroup( Action.Builder("action_id", "Action label", pendingIntent1) @@ -270,8 +270,8 @@ class SafetySourceIssueTest { context, 0, Intent("Other action PendingIntent"), - FLAG_IMMUTABLE - ) + FLAG_IMMUTABLE, + ), ) .setConfirmationDialogDetails(confirmationDialogDetails) .build() @@ -465,7 +465,7 @@ class SafetySourceIssueTest { fun notification_equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = Notification.CREATOR, - createCopy = { Notification.Builder(it).build() } + createCopy = { Notification.Builder(it).build() }, ) .addEqualityGroup( Notification.Builder("Title", "Text").build(), @@ -477,7 +477,7 @@ class SafetySourceIssueTest { .addEqualityGroup(Notification.Builder("Title", "Text").addAction(action2).build()) .addEqualityGroup( Notification.Builder("Title", "Text").addAction(action1).addAction(action2).build(), - Notification.Builder("Title", "Text").addActions(listOf(action1, action2)).build() + Notification.Builder("Title", "Text").addActions(listOf(action1, action2)).build(), ) .test() } @@ -538,7 +538,7 @@ class SafetySourceIssueTest { ) .addEqualityGroup( ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"), - ConfirmationDialogDetails("Title", "Text", "Accept", "Deny") + ConfirmationDialogDetails("Title", "Text", "Accept", "Deny"), ) .addEqualityGroup(ConfirmationDialogDetails("Other title", "Text", "Accept", "Deny")) .addEqualityGroup(ConfirmationDialogDetails("Title", "Other text", "Accept", "Deny")) @@ -555,7 +555,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -571,7 +571,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -587,7 +587,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -603,7 +603,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .addAction(action1) @@ -620,7 +620,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -637,7 +637,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -654,7 +654,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setAttributionTitle("attribution title") @@ -672,7 +672,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -689,7 +689,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) assertFails { safetySourceIssueBuilder.setAttributionTitle("title") } @@ -703,7 +703,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -719,7 +719,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -735,7 +735,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setIssueCategory(ISSUE_CATEGORY_DEVICE) @@ -753,7 +753,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) @@ -771,7 +771,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .addAction(action2) @@ -788,7 +788,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .addAction(action2) @@ -806,7 +806,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) val actions = safetySourceIssueBuilder.build().actions @@ -824,7 +824,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .addAction(action2) @@ -843,7 +843,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -859,7 +859,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setOnDismissPendingIntent(pendingIntentService) @@ -877,7 +877,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -894,7 +894,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setDeduplicationId("deduplication_id") @@ -912,7 +912,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -929,7 +929,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) assertFails { safetySourceIssueBuilder.setDeduplicationId("id") } @@ -943,7 +943,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -960,7 +960,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -977,7 +977,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setCustomNotification( @@ -1004,7 +1004,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1021,7 +1021,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) assertFails { safetySourceIssueBuilder.setCustomNotification(null) } @@ -1036,7 +1036,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1054,7 +1054,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER) @@ -1073,7 +1073,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1090,7 +1090,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = @@ -1110,7 +1110,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) assertFails { safetySourceIssueBuilder.setNotificationBehavior(0) } @@ -1125,7 +1125,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1143,7 +1143,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) @@ -1162,7 +1162,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1179,7 +1179,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = @@ -1199,7 +1199,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) assertFails { safetySourceIssueBuilder.setIssueActionability(0) } @@ -1213,7 +1213,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) } } @@ -1226,7 +1226,7 @@ class SafetySourceIssueTest { Generic.asNull(), "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) } } @@ -1239,7 +1239,7 @@ class SafetySourceIssueTest { "Issue title", Generic.asNull(), SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) } } @@ -1253,7 +1253,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_UNSPECIFIED, - "issue_type_id" + "issue_type_id", ) } assertThat(exception) @@ -1270,7 +1270,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", -1, - "issue_type_id" + "issue_type_id", ) } assertThat(exception) @@ -1286,7 +1286,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - Generic.asNull() + Generic.asNull(), ) } } @@ -1299,7 +1299,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = @@ -1319,7 +1319,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) @@ -1339,7 +1339,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = assertFailsWith(IllegalArgumentException::class) { @@ -1348,7 +1348,7 @@ class SafetySourceIssueTest { context, /* requestCode = */ 0, Intent("PendingIntent activity"), - FLAG_IMMUTABLE + FLAG_IMMUTABLE, ) ) } @@ -1365,7 +1365,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .addAction(action1) @@ -1385,7 +1385,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = @@ -1410,7 +1410,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .addAction(action2) @@ -1432,7 +1432,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) val exception = @@ -1452,7 +1452,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) .build() @@ -1469,7 +1469,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) .build() @@ -1485,7 +1485,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1505,7 +1505,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1526,7 +1526,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1553,7 +1553,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) @@ -1601,7 +1601,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1622,7 +1622,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1641,7 +1641,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1655,7 +1655,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1663,7 +1663,7 @@ class SafetySourceIssueTest { .addAction(action2) .setOnDismissPendingIntent(pendingIntentService) .setAttributionTitle("attribution title") - .build() + .build(), ) .addEqualityGroup( SafetySourceIssue.Builder( @@ -1671,7 +1671,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_CRITICAL_WARNING, - "issue_type_id" + "issue_type_id", ) .setAttributionTitle("Other issue attribution title") .addAction(action1) @@ -1683,7 +1683,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1698,7 +1698,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1707,7 +1707,7 @@ class SafetySourceIssueTest { .setOnDismissPendingIntent(pendingIntentService) .setAttributionTitle("attribution title") .setDeduplicationId("deduplication_id") - .build() + .build(), ) .addEqualityGroup( SafetySourceIssue.Builder( @@ -1715,7 +1715,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1732,7 +1732,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) @@ -1745,7 +1745,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) @@ -1758,7 +1758,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) @@ -1771,7 +1771,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL) .addAction(action1) @@ -1786,7 +1786,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) .addAction(action1) @@ -1796,11 +1796,11 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) .addAction(action1) - .build() + .build(), ) .addEqualityGroup( SafetySourceIssue.Builder( @@ -1808,7 +1808,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) .addAction(action1) @@ -1820,7 +1820,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) .build() @@ -1831,7 +1831,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED) .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) @@ -1843,7 +1843,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1864,7 +1864,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1889,7 +1889,7 @@ class SafetySourceIssueTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetySourceIssue.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup( SafetySourceIssue.Builder( @@ -1897,7 +1897,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -1910,14 +1910,14 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) .addAction(action1) .addAction(action2) .setOnDismissPendingIntent(pendingIntentService) - .build() + .build(), ) .addEqualityGroup( SafetySourceIssue.Builder( @@ -1925,7 +1925,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1936,7 +1936,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1947,7 +1947,7 @@ class SafetySourceIssueTest { "Other issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1958,7 +1958,7 @@ class SafetySourceIssueTest { "Issue title", "Different issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1969,7 +1969,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_CRITICAL_WARNING, - "issue_type_id" + "issue_type_id", ) .addAction(action1) .build() @@ -1980,7 +1980,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "other_issue_type_id" + "other_issue_type_id", ) .addAction(action1) .build() @@ -1991,7 +1991,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .addAction(action2) .build() @@ -2002,7 +2002,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(ISSUE_CATEGORY_ACCOUNT) @@ -2017,7 +2017,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(ISSUE_CATEGORY_DEVICE) @@ -2032,7 +2032,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(ISSUE_CATEGORY_DEVICE) @@ -2047,7 +2047,7 @@ class SafetySourceIssueTest { "Issue title", "Issue summary", SEVERITY_LEVEL_INFORMATION, - "issue_type_id" + "issue_type_id", ) .setSubtitle("Other issue subtitle") .setIssueCategory(ISSUE_CATEGORY_DEVICE) @@ -2058,7 +2058,7 @@ class SafetySourceIssueTest { context, 0, Intent("Other PendingIntent service"), - FLAG_IMMUTABLE + FLAG_IMMUTABLE, ) ) .build() @@ -2069,14 +2069,14 @@ class SafetySourceIssueTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = Action.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup( Action.Builder("action_id", "Action label", pendingIntent1).build(), Action.Builder("action_id", "Action label", pendingIntent1).build(), Action.Builder("action_id", "Action label", pendingIntent1) .setWillResolve(false) - .build() + .build(), ) .addEqualityGroup( Action.Builder("action_id", "Action label", pendingIntent1) @@ -2102,8 +2102,8 @@ class SafetySourceIssueTest { context, 0, Intent("Other action PendingIntent"), - FLAG_IMMUTABLE - ) + FLAG_IMMUTABLE, + ), ) .build() ) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt index 2a20cd45d..04a91b268 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt @@ -98,7 +98,7 @@ class SafetySourceStatusTest { EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = IconAction.CREATOR) .addEqualityGroup( IconAction(ICON_TYPE_GEAR, pendingIntent1), - IconAction(ICON_TYPE_GEAR, pendingIntent1) + IconAction(ICON_TYPE_GEAR, pendingIntent1), ) .addEqualityGroup(IconAction(ICON_TYPE_INFO, pendingIntent1)) .addEqualityGroup(IconAction(ICON_TYPE_GEAR, pendingIntent2)) @@ -195,7 +195,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( Generic.asNull(), "Status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) } } @@ -229,7 +229,7 @@ class SafetySourceStatusTest { context, /* requestCode = */ 0, Intent("PendingIntent service"), - FLAG_IMMUTABLE + FLAG_IMMUTABLE, ) ) } @@ -298,13 +298,13 @@ class SafetySourceStatusTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetySourceStatus.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup( SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .setPendingIntent(pendingIntent1) .setIconAction(iconAction1) @@ -313,18 +313,18 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .setPendingIntent(pendingIntent1) .setIconAction(iconAction1) .setEnabled(true) - .build() + .build(), ) .addEqualityGroup( SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .build() ) @@ -332,7 +332,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Other status title", "Status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .build() ) @@ -340,7 +340,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Other status summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .build() ) @@ -348,7 +348,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .build() ) @@ -356,7 +356,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setPendingIntent(pendingIntent2) .build() @@ -365,7 +365,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setIconAction(iconAction2) .build() @@ -374,7 +374,7 @@ class SafetySourceStatusTest { SafetySourceStatus.Builder( "Status title", "Status summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setEnabled(false) .build() 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 68dcd4a11..c3b6ef1b3 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt @@ -36,7 +36,7 @@ class SafetyCenterConfigTest { assertThat(BASE.safetySourcesGroups) .containsExactly( SafetySourcesGroupTest.STATELESS_INFERRED, - SafetySourcesGroupTest.HIDDEN_INFERRED + SafetySourcesGroupTest.HIDDEN_INFERRED, ) .inOrder() } @@ -65,7 +65,7 @@ class SafetyCenterConfigTest { assertThat(sourceGroups) .containsExactly( SafetySourcesGroupTest.STATELESS_INFERRED, - SafetySourcesGroupTest.HIDDEN_INFERRED + SafetySourcesGroupTest.HIDDEN_INFERRED, ) .inOrder() } @@ -99,14 +99,14 @@ class SafetyCenterConfigTest { ) = EqualsHashCodeToStringTester.ofParcelable( parcelableCreator = SafetyCenterConfig.CREATOR, - createCopy = createCopyFromBuilder + createCopy = createCopyFromBuilder, ) .addEqualityGroup( BASE, SafetyCenterConfig.Builder() .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED) .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) - .build() + .build(), ) .addEqualityGroup( SafetyCenterConfig.Builder() 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 4b6f0f6f9..210a5b5e7 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt @@ -368,7 +368,7 @@ class SafetySourceTest { { SafetySource.Builder(it).build() } } else { null - } + }, ) .addEqualityGroup(DYNAMIC_BAREBONE) .addEqualityGroup( @@ -396,7 +396,7 @@ class SafetySourceTest { setTitleForPrivateProfileResId(REFERENCE_RES_ID) } } - .build() + .build(), ) .addEqualityGroup(DYNAMIC_HIDDEN) .addEqualityGroup(DYNAMIC_HIDDEN_WITH_SEARCH) 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 f741369eb..06aad5d40 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt @@ -155,7 +155,7 @@ class SafetySourcesGroupTest { .containsExactly( SafetySourceTest.DYNAMIC_BAREBONE, SafetySourceTest.STATIC_BAREBONE, - SafetySourceTest.ISSUE_ONLY_BAREBONE + SafetySourceTest.ISSUE_ONLY_BAREBONE, ) .inOrder() assertThat(STATELESS_INFERRED.safetySources) @@ -169,7 +169,7 @@ class SafetySourcesGroupTest { .containsExactly( SafetySourceTest.DYNAMIC_BAREBONE, SafetySourceTest.STATIC_BAREBONE, - SafetySourceTest.ISSUE_ONLY_BAREBONE + SafetySourceTest.ISSUE_ONLY_BAREBONE, ) assertThat(STATELESS_BAREBONE.safetySources) .containsExactly(SafetySourceTest.STATIC_BAREBONE) @@ -177,7 +177,7 @@ class SafetySourcesGroupTest { .containsExactly( SafetySourceTest.DYNAMIC_BAREBONE, SafetySourceTest.STATIC_BAREBONE, - SafetySourceTest.ISSUE_ONLY_BAREBONE + SafetySourceTest.ISSUE_ONLY_BAREBONE, ) assertThat(HIDDEN_BAREBONE.safetySources) .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) @@ -325,7 +325,7 @@ class SafetySourcesGroupTest { { SafetySourcesGroup.Builder(it).build() } } else { null - } + }, ) .addEqualityGroup(STATEFUL_INFERRED_WITH_SUMMARY) .addEqualityGroup(STATEFUL_INFERRED_WITH_ICON) @@ -340,7 +340,7 @@ class SafetySourcesGroupTest { .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) - .build() + .build(), ) .apply { if (SdkLevel.isAtLeastU()) add(STATEFUL_ALL_OPTIONAL) } .toTypedArray() diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt index 7c9e2cffc..34f5c1f69 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/XmlConfigTest.kt @@ -88,7 +88,7 @@ class XmlConfigTest { private fun parseXmlConfig() = SafetyCenterConfigParser.parseXmlResource( safetyCenterResourcesApk.safetyCenterConfig!!, - safetyCenterResourcesApk.resources + safetyCenterResourcesApk.resources, ) companion object { diff --git a/tests/functional/safetycenter/multiusers/Android.bp b/tests/functional/safetycenter/multiusers/Android.bp index 30024221b..745e763f0 100644 --- a/tests/functional/safetycenter/multiusers/Android.bp +++ b/tests/functional/safetycenter/multiusers/Android.bp @@ -36,6 +36,7 @@ android_test { "Harrier", "Nene", "TestApp", + "bedstead-enterprise", "com.android.permission.flags-aconfig-java-export", ], test_suites: [ diff --git a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt index 9d6636fc3..9dba6f9c2 100644 --- a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt +++ b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt @@ -41,15 +41,20 @@ import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceData import androidx.test.core.app.ApplicationProvider import androidx.test.filters.LargeTest -import com.android.bedstead.harrier.BedsteadJUnit4 -import com.android.bedstead.harrier.DeviceState -import com.android.bedstead.harrier.annotations.EnsureHasAdditionalUser -import com.android.bedstead.harrier.annotations.EnsureHasCloneProfile -import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile -import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile -import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile import com.android.bedstead.enterprise.annotations.EnsureHasDeviceOwner import com.android.bedstead.enterprise.annotations.EnsureHasNoDeviceOwner +import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile +import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile +import com.android.bedstead.enterprise.workProfile +import com.android.bedstead.harrier.BedsteadJUnit4 +import com.android.bedstead.harrier.DeviceState +import com.android.bedstead.multiuser.additionalUser +import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser +import com.android.bedstead.multiuser.annotations.EnsureHasCloneProfile +import com.android.bedstead.multiuser.annotations.EnsureHasNoPrivateProfile +import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile +import com.android.bedstead.multiuser.cloneProfile +import com.android.bedstead.multiuser.privateProfile import com.android.bedstead.nene.TestApis import com.android.bedstead.nene.types.OptionalBoolean.TRUE import com.android.compatibility.common.util.DisableAnimationRule @@ -131,30 +136,30 @@ class SafetyCenterMultiUsersTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueCritical( ISSUE_ONLY_BAREBONE_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID - ) + groupId = MIXED_STATELESS_GROUP_ID, + ), ) private val dynamicBareboneDefault: SafetyCenterEntry @@ -180,7 +185,7 @@ class SafetyCenterMultiUsersTest { .safetyCenterEntryDefaultBuilder( DYNAMIC_DISABLED_ID, userId = deviceState.workProfile().id(), - title = "Paste" + title = "Paste", ) .setPendingIntent(null) .setEnabled(false) @@ -195,11 +200,9 @@ class SafetyCenterMultiUsersTest { DYNAMIC_DISABLED_ID, deviceState.workProfile().id(), title = "Ok title for Work", - pendingIntent = null - ) - .setSummary( - safetyCenterResourcesApk.getStringByName("work_profile_paused"), + pendingIntent = null, ) + .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setEnabled(false) .build() @@ -211,7 +214,7 @@ class SafetyCenterMultiUsersTest { get() = safetyCenterTestData.safetyCenterEntryUnspecified( DYNAMIC_HIDDEN_ID, - pendingIntent = null + pendingIntent = null, ) private val dynamicHiddenForWorkUpdated: SafetyCenterEntry @@ -224,11 +227,9 @@ class SafetyCenterMultiUsersTest { DYNAMIC_HIDDEN_ID, deviceState.workProfile().id(), title = "Ok title for Work", - pendingIntent = null - ) - .setSummary( - safetyCenterResourcesApk.getStringByName("work_profile_paused"), + pendingIntent = null, ) + .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setEnabled(false) .build() @@ -261,12 +262,12 @@ class SafetyCenterMultiUsersTest { .safetyCenterEntryDefaultStaticBuilder( STATIC_ALL_OPTIONAL_ID, userId = deviceState.workProfile().id(), - title = "Paste" + title = "Paste", ) .setPendingIntent( createTestActivityRedirectPendingIntentForUser( deviceState.workProfile().userHandle(), - explicit = false + explicit = false, ) ) @@ -286,12 +287,12 @@ class SafetyCenterMultiUsersTest { .safetyCenterEntryDefaultStaticBuilder( STATIC_ALL_OPTIONAL_ID, userId = deviceState.privateProfile().id(), - title = "Unknown" + title = "Unknown", ) .setPendingIntent( createTestActivityRedirectPendingIntentForUser( deviceState.privateProfile().userHandle(), - explicit = false + explicit = false, ) ) @@ -319,20 +320,20 @@ class SafetyCenterMultiUsersTest { .setPendingIntent( createTestActivityRedirectPendingIntentForUser( deviceState.workProfile().userHandle(), - explicit + explicit, ) ) private fun staticEntryForPrivateBuilder( title: CharSequence = "Unknown", - explicit: Boolean = true + explicit: Boolean = true, ) = SafetyCenterStaticEntry.Builder(title) .setSummary("OK") .setPendingIntent( createTestActivityRedirectPendingIntentForUser( deviceState.privateProfile().userHandle(), - explicit + explicit, ) ) @@ -380,11 +381,11 @@ class SafetyCenterMultiUsersTest { pendingIntent = createTestActivityRedirectPendingIntentForUser( deviceState.additionalUser().userHandle() - ) + ), ) ) ), - emptyList() + emptyList(), ) @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) @@ -448,7 +449,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.workProfile().userHandle()) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) setQuietMode(true) @@ -469,7 +470,7 @@ class SafetyCenterMultiUsersTest { val dataForAdditionalUser = safetySourceTestData.information additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForAdditionalUser + dataForAdditionalUser, ) checkState( additionalUserSafetyCenterManager.getSafetySourceDataWithInteractAcrossUsersPermission( @@ -509,7 +510,7 @@ class SafetyCenterMultiUsersTest { val dataForWork = safetySourceTestData.informationWithIssueForWork managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) TestApis.packages().find(context.packageName).uninstall(deviceState.workProfile()) @@ -530,7 +531,7 @@ class SafetyCenterMultiUsersTest { val dataForWork = safetySourceTestData.informationWithIssueForWork managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) deviceState.workProfile().remove() @@ -551,7 +552,7 @@ class SafetyCenterMultiUsersTest { val dataForWork = safetySourceTestData.informationWithIssueForWork managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) setQuietMode(true) @@ -565,6 +566,7 @@ class SafetyCenterMultiUsersTest { @Test @EnsureHasNoWorkProfile + @EnsureHasNoPrivateProfile fun getSafetyCenterData_withComplexConfigWithoutWorkProfile_returnsPrimaryDataFromConfig() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig) @@ -591,20 +593,21 @@ class SafetyCenterMultiUsersTest { staticGroupBuilder .setEntries(listOf(staticBarebone, staticAllOptional)) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( "OK", - listOf(createStaticEntry(), createStaticEntry(explicit = false)) + listOf(createStaticEntry(), createStaticEntry(explicit = false)), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @Test @EnsureHasWorkProfile(installInstrumentedApp = TRUE) + @EnsureHasNoPrivateProfile fun getSafetyCenterData_withComplexConfigWithoutDataProvided_returnsDataFromConfig() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig) @@ -625,7 +628,7 @@ class SafetyCenterMultiUsersTest { listOf( dynamicBareboneDefault, dynamicDisabledDefault, - dynamicDisabledForWorkDefault + dynamicDisabledForWorkDefault, ) ) .setSeverityUnspecifiedIconType( @@ -639,7 +642,7 @@ class SafetyCenterMultiUsersTest { listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -648,16 +651,17 @@ class SafetyCenterMultiUsersTest { createStaticEntry(), createStaticEntryForWork(), createStaticEntry(explicit = false), - createStaticEntryForWork(explicit = false) - ) + createStaticEntryForWork(explicit = false), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @Test @EnsureHasWorkProfile(installInstrumentedApp = TRUE) + @EnsureHasNoPrivateProfile fun getSafetyCenterData_withComplexConfigWithPrimaryDataProvided_returnsPrimaryDataProvided() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig) updatePrimaryProfileSources() @@ -678,7 +682,7 @@ class SafetyCenterMultiUsersTest { dynamicBareboneUpdated, dynamicDisabledUpdated, dynamicDisabledForWorkDefault, - dynamicHiddenUpdated + dynamicHiddenUpdated, ) ) .setSeverityUnspecifiedIconType( @@ -692,7 +696,7 @@ class SafetyCenterMultiUsersTest { listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -701,16 +705,17 @@ class SafetyCenterMultiUsersTest { staticEntryUpdated, createStaticEntryForWork(), createStaticEntry(explicit = false), - createStaticEntryForWork(explicit = false) - ) + createStaticEntryForWork(explicit = false), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @Test @EnsureHasWorkProfile(installInstrumentedApp = TRUE) + @EnsureHasNoPrivateProfile fun getSafetyCenterData_withComplexConfigWithExtraWorkOnlyWithAllDataProvided_returnsAllDataProvided() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig) updatePrimaryProfileSources() @@ -725,56 +730,56 @@ class SafetyCenterMultiUsersTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueCritical( ISSUE_ONLY_BAREBONE_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_DISABLED_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_HIDDEN_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID - ) + groupId = MIXED_STATELESS_GROUP_ID, + ), ), listOf( SafetyCenterEntryOrGroup( @@ -787,7 +792,7 @@ class SafetyCenterMultiUsersTest { dynamicDisabledUpdated, dynamicDisabledForWorkUpdated, dynamicHiddenUpdated, - dynamicHiddenForWorkUpdated + dynamicHiddenForWorkUpdated, ) ) .setSeverityUnspecifiedIconType( @@ -801,7 +806,7 @@ class SafetyCenterMultiUsersTest { listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -810,10 +815,10 @@ class SafetyCenterMultiUsersTest { staticEntryUpdated, staticEntryForWorkUpdated, createStaticEntry(explicit = false), - createStaticEntryForWork(explicit = false) - ) + createStaticEntryForWork(explicit = false), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -837,56 +842,56 @@ class SafetyCenterMultiUsersTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueCritical( ISSUE_ONLY_BAREBONE_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_DISABLED_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_HIDDEN_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID - ) + groupId = MIXED_STATELESS_GROUP_ID, + ), ), listOf( SafetyCenterEntryOrGroup( @@ -899,7 +904,7 @@ class SafetyCenterMultiUsersTest { dynamicDisabledUpdated, dynamicDisabledForWorkUpdated, dynamicHiddenUpdated, - dynamicHiddenForWorkUpdated + dynamicHiddenForWorkUpdated, ) ) .setSeverityUnspecifiedIconType( @@ -913,7 +918,7 @@ class SafetyCenterMultiUsersTest { listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -922,10 +927,10 @@ class SafetyCenterMultiUsersTest { staticEntryUpdated, staticEntryForWorkUpdated, createStaticEntry(explicit = false), - createStaticEntryForWork(explicit = false) - ) + createStaticEntryForWork(explicit = false), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -951,82 +956,82 @@ class SafetyCenterMultiUsersTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueCritical( ISSUE_ONLY_BAREBONE_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_DISABLED_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_HIDDEN_ID, managedUserId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, managedUserId, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_DISABLED_ID, privateProfileId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_HIDDEN_ID, privateProfileId, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_ALL_OPTIONAL_ID, privateProfileId, attributionTitle = null, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, privateProfileId, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, privateProfileId, - groupId = MIXED_STATELESS_GROUP_ID - ) + groupId = MIXED_STATELESS_GROUP_ID, + ), ), listOf( SafetyCenterEntryOrGroup( @@ -1041,7 +1046,7 @@ class SafetyCenterMultiUsersTest { dynamicDisabledForPrivateUpdated, dynamicHiddenUpdated, dynamicHiddenForWorkUpdated, - dynamicHiddenForPrivateUpdated + dynamicHiddenForPrivateUpdated, ) ) .setSeverityUnspecifiedIconType( @@ -1056,11 +1061,11 @@ class SafetyCenterMultiUsersTest { staticBarebone, staticAllOptional, staticAllOptionalForWork, - staticAllOptionalForPrivate + staticAllOptionalForPrivate, ) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -1071,16 +1076,17 @@ class SafetyCenterMultiUsersTest { staticEntryForPrivateUpdated, createStaticEntry(explicit = false), createStaticEntryForWork(explicit = false), - createStaticEntryForPrivate(explicit = false) - ) + createStaticEntryForPrivate(explicit = false), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @Test @EnsureHasWorkProfile(installInstrumentedApp = TRUE) + @EnsureHasNoPrivateProfile fun getSafetyCenterData_withQuietMode_shouldHaveWorkProfilePausedSummaryAndNoWorkIssues() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig) updatePrimaryProfileSources() @@ -1118,11 +1124,11 @@ class SafetyCenterMultiUsersTest { listOf( staticBarebone, staticAllOptional, - staticAllOptionalForWorkPaused + staticAllOptionalForWorkPaused, ) ) .build() - ) + ), ), listOf( SafetyCenterStaticEntryGroup( @@ -1131,10 +1137,10 @@ class SafetyCenterMultiUsersTest { staticEntryUpdated, staticEntryForWorkPausedUpdated, createStaticEntry(explicit = false), - createStaticEntryForWorkPaused() - ) + createStaticEntryForWorkPaused(), + ), ) - ) + ), ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -1151,7 +1157,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.workProfile().userHandle()) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForPrimaryUserWorkProfile + dataForPrimaryUserWorkProfile, ) val additionalUserSafetyCenterManager = @@ -1165,6 +1171,7 @@ class SafetyCenterMultiUsersTest { @Test @EnsureHasWorkProfile(installInstrumentedApp = TRUE) + @EnsureHasNoPrivateProfile fun getSafetyCenterData_afterManagedProfileRemoved_returnsDefaultData() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig) val managedSafetyCenterManager = @@ -1192,8 +1199,8 @@ class SafetyCenterMultiUsersTest { pendingIntent = createTestActivityRedirectPendingIntentForUser( deviceState.workProfile().userHandle() - ) - ) + ), + ), ) ) .setSeverityUnspecifiedIconType( @@ -1202,7 +1209,7 @@ class SafetyCenterMultiUsersTest { .build() ) ), - emptyList() + emptyList(), ) checkState( safetyCenterManager.getSafetyCenterDataWithPermission() == @@ -1224,7 +1231,7 @@ class SafetyCenterMultiUsersTest { safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID) ) ), - emptyList() + emptyList(), ) assertThat(safetyCenterManager.getSafetyCenterDataWithPermission()) .isEqualTo(safetyCenterDataForPrimaryUser) @@ -1264,8 +1271,8 @@ class SafetyCenterMultiUsersTest { pendingIntent = createTestActivityRedirectPendingIntentForUser( deviceState.privateProfile().userHandle() - ) - ) + ), + ), ) ) .setSeverityUnspecifiedIconType( @@ -1274,7 +1281,7 @@ class SafetyCenterMultiUsersTest { .build() ) ), - emptyList() + emptyList(), ) assertThat(safetyCenterManager.getSafetyCenterDataWithPermission()) @@ -1295,7 +1302,7 @@ class SafetyCenterMultiUsersTest { safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID) ) ), - emptyList() + emptyList(), ) assertThat(safetyCenterManager.getSafetyCenterDataWithPermission()) .isEqualTo(safetyCenterDataForPrimaryUser) @@ -1338,7 +1345,7 @@ class SafetyCenterMultiUsersTest { assertFailsWith(IllegalArgumentException::class) { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_ALL_OPTIONAL_ID, - dataForWork + dataForWork, ) } } @@ -1355,7 +1362,7 @@ class SafetyCenterMultiUsersTest { managedSafetyCenterManager.setSafetySourceData( SINGLE_SOURCE_ALL_PROFILE_ID, dataForWork, - EVENT_SOURCE_STATE_CHANGED + EVENT_SOURCE_STATE_CHANGED, ) } } @@ -1371,7 +1378,7 @@ class SafetyCenterMultiUsersTest { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) val safetySourceData = @@ -1392,7 +1399,7 @@ class SafetyCenterMultiUsersTest { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) val safetySourceData = @@ -1412,7 +1419,7 @@ class SafetyCenterMultiUsersTest { clonedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForClone + dataForClone, ) val safetySourceData = @@ -1433,7 +1440,7 @@ class SafetyCenterMultiUsersTest { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) val safetySourceData = @@ -1457,7 +1464,7 @@ class SafetyCenterMultiUsersTest { SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_ALL_PROFILE_SOURCE_ID, - dataForWorkProfile + dataForWorkProfile, ) val apiSafetySourceData = @@ -1481,7 +1488,7 @@ class SafetyCenterMultiUsersTest { assertFailsWith(IllegalArgumentException::class) { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ID, - dataForWork + dataForWork, ) } } @@ -1498,7 +1505,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.workProfile().userHandle()) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) val apiSafetySourceData = @@ -1525,14 +1532,14 @@ class SafetyCenterMultiUsersTest { managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ALL_PROFILE_ID, - dataForWork + dataForWork, ) TestNotificationListener.waitForNotificationsMatching( NotificationCharacteristics( title = "Information issue title", text = "Information issue summary", - actions = listOf("Review") + actions = listOf("Review"), ) ) } @@ -1548,7 +1555,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle()) additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ID, - dataForPrimaryUser + dataForPrimaryUser, ) val apiSafetySourceData = @@ -1570,7 +1577,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle()) additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ID, - dataForAdditionalUser + dataForAdditionalUser, ) val apiSafetySourceDataForPrimaryUser = @@ -1593,7 +1600,7 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.additionalUser().userHandle()) additionalUserSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( SINGLE_SOURCE_ID, - dataForAdditionalUser + dataForAdditionalUser, ) val apiSafetySourceDataForPrimaryUser = @@ -1620,12 +1627,12 @@ class SafetyCenterMultiUsersTest { private fun createTestActivityRedirectPendingIntentForUser( user: UserHandle, - explicit: Boolean = true + explicit: Boolean = true, ): PendingIntent { return callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) { SafetySourceTestData.createRedirectPendingIntent( getContextForUser(user), - SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit) + SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit), ) } } @@ -1640,7 +1647,7 @@ class SafetyCenterMultiUsersTest { private fun SafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( id: String, dataToSet: SafetySourceData, - safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED + safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED, ) = callWithShellPermissionIdentity(INTERACT_ACROSS_USERS_FULL) { setSafetySourceDataWithPermission(id, dataToSet, safetyEvent) @@ -1684,28 +1691,28 @@ class SafetyCenterMultiUsersTest { private fun updatePrimaryProfileSources() { safetyCenterTestHelper.setData( DYNAMIC_BAREBONE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( DYNAMIC_DISABLED_ID, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, safetySourceTestData.unspecified) safetyCenterTestHelper.setData( ISSUE_ONLY_BAREBONE_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue), ) safetyCenterTestHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), ) safetyCenterTestHelper.setData( DYNAMIC_IN_STATELESS_ID, - safetySourceTestData.unspecifiedWithIssue + safetySourceTestData.unspecifiedWithIssue, ) safetyCenterTestHelper.setData( ISSUE_ONLY_IN_STATELESS_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) } @@ -1714,23 +1721,23 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.workProfile().userHandle()) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_DISABLED_ID, - safetySourceTestData.informationWithIssueForWork + safetySourceTestData.informationWithIssueForWork, ) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_HIDDEN_ID, - safetySourceTestData.informationWithIssueForWork + safetySourceTestData.informationWithIssueForWork, ) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_IN_STATELESS_ID, - safetySourceTestData.unspecifiedWithIssueForWork + safetySourceTestData.unspecifiedWithIssueForWork, ) managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_IN_STATELESS_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) } @@ -1739,23 +1746,23 @@ class SafetyCenterMultiUsersTest { getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle()) privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_DISABLED_ID, - safetySourceTestData.informationWithIssueForPrivate + safetySourceTestData.informationWithIssueForPrivate, ) privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_HIDDEN_ID, - safetySourceTestData.informationWithIssueForPrivate + safetySourceTestData.informationWithIssueForPrivate, ) privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( DYNAMIC_IN_STATELESS_ID, - safetySourceTestData.unspecifiedWithIssueForPrivate + safetySourceTestData.unspecifiedWithIssueForPrivate, ) privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission( ISSUE_ONLY_IN_STATELESS_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) } } diff --git a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt index 73f435615..7e8205bd6 100644 --- a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt +++ b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt @@ -120,7 +120,7 @@ class SafetyCenterActivityTest { context.getString(safetyCenterTestConfigs.staticSource1.summaryResId), context.getString(safetyCenterTestConfigs.staticSourceGroup2.titleResId), context.getString(safetyCenterTestConfigs.staticSource2.titleResId), - context.getString(safetyCenterTestConfigs.staticSource2.summaryResId) + context.getString(safetyCenterTestConfigs.staticSource2.summaryResId), ) } } @@ -144,8 +144,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, entrySummary = SAFETY_SOURCE_1_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_2, @@ -153,8 +153,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_2_TITLE, entrySummary = SAFETY_SOURCE_2_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_3, @@ -162,8 +162,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, entrySummary = SAFETY_SOURCE_3_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_4, @@ -171,8 +171,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_4_TITLE, entrySummary = SAFETY_SOURCE_4_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_5, @@ -180,8 +180,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_5_TITLE, entrySummary = SAFETY_SOURCE_5_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) } @@ -190,7 +190,7 @@ class SafetyCenterActivityTest { context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.titleResId), context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.summaryResId), context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.titleResId), - context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.summaryResId) + context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.summaryResId), ) waitAllTextNotDisplayed( SAFETY_SOURCE_1_TITLE, @@ -200,7 +200,7 @@ class SafetyCenterActivityTest { SAFETY_SOURCE_4_TITLE, SAFETY_SOURCE_4_SUMMARY, SAFETY_SOURCE_5_TITLE, - SAFETY_SOURCE_5_SUMMARY + SAFETY_SOURCE_5_SUMMARY, ) } } @@ -215,8 +215,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, entrySummary = SAFETY_SOURCE_1_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_2, @@ -224,8 +224,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_RECOMMENDATION, entryTitle = SAFETY_SOURCE_2_TITLE, entrySummary = SAFETY_SOURCE_2_SUMMARY, - withIssue = true - ) + withIssue = true, + ), ) setData( SOURCE_ID_3, @@ -233,8 +233,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, entrySummary = SAFETY_SOURCE_3_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_4, @@ -242,8 +242,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_RECOMMENDATION, entryTitle = SAFETY_SOURCE_4_TITLE, entrySummary = SAFETY_SOURCE_4_SUMMARY, - withIssue = true - ) + withIssue = true, + ), ) setData( SOURCE_ID_5, @@ -251,8 +251,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING, entryTitle = SAFETY_SOURCE_5_TITLE, entrySummary = SAFETY_SOURCE_5_SUMMARY, - withIssue = true - ) + withIssue = true, + ), ) } @@ -261,7 +261,7 @@ class SafetyCenterActivityTest { context.getString(safetyCenterTestConfigs.dynamicSourceGroup1.titleResId), SAFETY_SOURCE_2_SUMMARY, context.getString(safetyCenterTestConfigs.dynamicSourceGroup3.titleResId), - SAFETY_SOURCE_5_SUMMARY + SAFETY_SOURCE_5_SUMMARY, ) waitAllTextNotDisplayed( SAFETY_SOURCE_1_TITLE, @@ -269,7 +269,7 @@ class SafetyCenterActivityTest { SAFETY_SOURCE_1_SUMMARY, SAFETY_SOURCE_4_TITLE, SAFETY_SOURCE_5_TITLE, - SAFETY_SOURCE_4_SUMMARY + SAFETY_SOURCE_4_SUMMARY, ) } } @@ -358,7 +358,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.information) @@ -412,7 +412,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect + safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect, ) context.launchSafetyCenterActivity { @@ -427,7 +427,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect + safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect, ) context.launchSafetyCenterActivity { @@ -489,7 +489,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithIconAction + safetySourceTestData.informationWithIconAction, ) context.launchSafetyCenterActivity { @@ -523,7 +523,7 @@ class SafetyCenterActivityTest { val issue = safetySourceTestData.recommendationGeneralIssue safetyCenterTestHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(issue) + SafetySourceTestData.issuesOnly(issue), ) context.launchSafetyCenterActivity { waitDisplayed(By.desc("Alert. ${issue.title}")) } @@ -563,7 +563,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) context.launchSafetyCenterActivity { @@ -581,21 +581,21 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) context.launchSafetyCenterActivity { clickDismissIssueCard() waitAllTextDisplayed( "Dismiss this alert?", - "Review your security and privacy settings any time to add more protection" + "Review your security and privacy settings any time to add more protection", ) getUiDevice().rotate() waitAllTextDisplayed( "Dismiss this alert?", - "Review your security and privacy settings any time to add more protection" + "Review your security and privacy settings any time to add more protection", ) clickConfirmDismissal() @@ -609,7 +609,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) context.launchSafetyCenterActivity { @@ -626,7 +626,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) context.launchSafetyCenterActivity { @@ -650,13 +650,13 @@ class SafetyCenterActivityTest { // Set the initial data for the source safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage + safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage, ) // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -680,13 +680,13 @@ class SafetyCenterActivityTest { // Set the initial data for the source safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage + safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage, ) // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -708,13 +708,13 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation + safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation, ) // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -734,13 +734,13 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation + safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation, ) // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -764,7 +764,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation + safetySourceTestData.criticalWithResolvingGeneralIssueWithConfirmation, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -786,13 +786,13 @@ class SafetyCenterActivityTest { // Set the initial data for the source safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -909,11 +909,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -923,7 +923,7 @@ class SafetyCenterActivityTest { waitExpandedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -933,11 +933,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -948,7 +948,7 @@ class SafetyCenterActivityTest { waitCollapsedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -958,11 +958,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.criticalWithRedirectingIssue + safetySourceTestData.criticalWithRedirectingIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -973,7 +973,7 @@ class SafetyCenterActivityTest { waitCollapsedIssuesDisplayed( safetySourceTestData.criticalRedirectingIssue, safetySourceTestData.criticalResolvingGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -983,11 +983,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1007,11 +1007,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1022,7 +1022,7 @@ class SafetyCenterActivityTest { waitCollapsedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -1032,7 +1032,7 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) context.launchSafetyCenterActivity { @@ -1046,11 +1046,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1058,7 +1058,7 @@ class SafetyCenterActivityTest { waitCollapsedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -1068,11 +1068,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1084,7 +1084,7 @@ class SafetyCenterActivityTest { waitExpandedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -1094,11 +1094,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1118,7 +1118,7 @@ class SafetyCenterActivityTest { waitExpandedIssuesDisplayed( safetySourceTestData.criticalResolvingGeneralIssue, safetySourceTestData.recommendationGeneralIssue, - safetySourceTestData.informationIssue + safetySourceTestData.informationIssue, ) } } @@ -1128,11 +1128,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1152,11 +1152,11 @@ class SafetyCenterActivityTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(SOURCE_ID_3, safetySourceTestData.informationWithIssue) @@ -1183,8 +1183,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, entrySummary = SAFETY_SOURCE_1_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_2, @@ -1192,8 +1192,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_2_TITLE, entrySummary = SAFETY_SOURCE_2_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_3, @@ -1201,8 +1201,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, entrySummary = SAFETY_SOURCE_3_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_4, @@ -1210,8 +1210,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_4_TITLE, entrySummary = SAFETY_SOURCE_4_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_5, @@ -1219,8 +1219,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_5_TITLE, entrySummary = SAFETY_SOURCE_5_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) } @@ -1238,7 +1238,7 @@ class SafetyCenterActivityTest { SAFETY_SOURCE_1_TITLE, SAFETY_SOURCE_1_SUMMARY, SAFETY_SOURCE_2_TITLE, - SAFETY_SOURCE_2_SUMMARY + SAFETY_SOURCE_2_SUMMARY, ) } } @@ -1253,8 +1253,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, entrySummary = SAFETY_SOURCE_1_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_2, @@ -1262,8 +1262,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_2_TITLE, entrySummary = SAFETY_SOURCE_2_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_3, @@ -1271,8 +1271,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, entrySummary = SAFETY_SOURCE_3_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_4, @@ -1280,8 +1280,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_4_TITLE, entrySummary = SAFETY_SOURCE_4_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_5, @@ -1289,8 +1289,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_5_TITLE, entrySummary = SAFETY_SOURCE_5_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) } @@ -1332,7 +1332,7 @@ class SafetyCenterActivityTest { ) waitAllTextDisplayed( context.getString(safetyCenterTestConfigs.dynamicSource1.titleResId), - context.getString(safetyCenterTestConfigs.dynamicSource2.titleResId) + context.getString(safetyCenterTestConfigs.dynamicSource2.titleResId), ) } } @@ -1348,8 +1348,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, entrySummary = SAFETY_SOURCE_1_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_2, @@ -1357,8 +1357,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_2_TITLE, entrySummary = SAFETY_SOURCE_2_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_3, @@ -1366,8 +1366,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, entrySummary = SAFETY_SOURCE_3_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_4, @@ -1375,8 +1375,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_4_TITLE, entrySummary = SAFETY_SOURCE_4_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) setData( SOURCE_ID_5, @@ -1384,8 +1384,8 @@ class SafetyCenterActivityTest { severityLevel = SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_5_TITLE, entrySummary = SAFETY_SOURCE_5_SUMMARY, - withIssue = false - ) + withIssue = false, + ), ) } @@ -1425,7 +1425,7 @@ class SafetyCenterActivityTest { context.launchSafetyCenterActivity { waitAllTextDisplayed( context.getString(lastGroup.titleResId), - context.getString(lastGroup.summaryResId) + context.getString(lastGroup.summaryResId), ) waitDisplayed(By.text(context.getString(firstGroup.titleResId))) { it.click() } diff --git a/tests/functional/safetycenter/singleuser/AndroidTest.xml b/tests/functional/safetycenter/singleuser/AndroidTest.xml index 3aa173508..af040eb6f 100644 --- a/tests/functional/safetycenter/singleuser/AndroidTest.xml +++ b/tests/functional/safetycenter/singleuser/AndroidTest.xml @@ -47,6 +47,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- TODO(b/379928062): Ensure device not on lockscreen. Reassess when keyguard bug is + closed --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt index 4f06c0f3f..985f15572 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt @@ -22,6 +22,9 @@ import android.content.Intent import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.UserHandle +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntry import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING @@ -64,6 +67,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.android.compatibility.common.util.SystemUtil import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.safetycenter.internaldata.SafetyCenterBundles import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY import com.android.safetycenter.internaldata.SafetyCenterEntryId @@ -156,7 +160,7 @@ class SafetyCenterManagerTest { get() = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -165,7 +169,7 @@ class SafetyCenterManagerTest { get() = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName("scanning_title"), - safetyCenterResourcesApk.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("loading_summary"), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN) .setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS) @@ -175,7 +179,7 @@ class SafetyCenterManagerTest { get() = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -186,7 +190,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_ok_review_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -199,7 +203,7 @@ class SafetyCenterManagerTest { ), safetyCenterResourcesApk.getStringByName( "overall_severity_level_ok_review_summary" - ) + ), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -210,7 +214,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_safety_recommendation_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) .build() @@ -221,7 +225,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_account_recommendation_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) .build() @@ -232,7 +236,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_device_recommendation_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) .build() @@ -243,7 +247,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_safety_warning_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -254,7 +258,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_safety_warning_title" ), - safetyCenterTestData.getAlertString(2) + safetyCenterTestData.getAlertString(2), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -265,7 +269,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_account_warning_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -276,7 +280,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_account_warning_title" ), - safetyCenterTestData.getAlertString(2) + safetyCenterTestData.getAlertString(2), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -287,7 +291,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_device_warning_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -298,7 +302,7 @@ class SafetyCenterManagerTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_device_warning_title" ), - safetyCenterTestData.getAlertString(2) + safetyCenterTestData.getAlertString(2), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -326,7 +330,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID), SafetyCenterEntry.Builder( SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID), - "OK" + "OK", ) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setSummary("OK") @@ -338,7 +342,7 @@ class SafetyCenterManagerTest { .setSeverityUnspecifiedIconType( SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON ) - .build() + .build(), ) ) .setSeverityUnspecifiedIconType( @@ -366,8 +370,8 @@ class SafetyCenterManagerTest { explicit = false ) ) - .build() - ) + .build(), + ), ) private val safetyCenterStaticEntryGroupMixedFromComplexConfig: SafetyCenterStaticEntryGroup @@ -388,8 +392,8 @@ class SafetyCenterManagerTest { explicit = false ) ) - .build() - ) + .build(), + ), ) private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig: @@ -411,8 +415,8 @@ class SafetyCenterManagerTest { explicit = false ) ) - .build() - ) + .build(), + ), ) private val safetyCenterDataFromConfigScanning: SafetyCenterData @@ -425,7 +429,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataFromConfig: SafetyCenterData @@ -438,7 +442,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataUnspecified: SafetyCenterData @@ -451,7 +455,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataOk: SafetyCenterData @@ -464,7 +468,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataOkWithIconAction: SafetyCenterData @@ -478,12 +482,12 @@ class SafetyCenterManagerTest { .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID) .setIconAction( ICON_ACTION_TYPE_INFO, - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) .build() ) ), - emptyList() + emptyList(), ) private val safetyCenterDataUnknownScanningWithError: SafetyCenterData @@ -496,7 +500,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataUnknownReviewError: SafetyCenterData @@ -509,7 +513,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataOkOneAlert: SafetyCenterData @@ -522,7 +526,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataOkReviewCriticalEntry: SafetyCenterData @@ -531,7 +535,7 @@ class SafetyCenterManagerTest { safetyCenterStatusOkReview, emptyList(), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataOkReviewRecommendationEntry: SafetyCenterData @@ -540,7 +544,7 @@ class SafetyCenterManagerTest { safetyCenterStatusOkReview, emptyList(), listOf(safetyCenterEntryOrGroupRecommendation), - emptyList() + emptyList(), ) private val safetyCenterDataOkReviewOneAlert: SafetyCenterData @@ -549,7 +553,7 @@ class SafetyCenterManagerTest { safetyCenterStatusOkReviewOneAlert, listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataGeneralRecommendationOneAlert: SafetyCenterData @@ -562,7 +566,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataGeneralRecommendationAlertWithConfirmation: SafetyCenterData @@ -572,7 +576,7 @@ class SafetyCenterManagerTest { listOf( safetyCenterTestData.safetyCenterIssueRecommendation( SINGLE_SOURCE_ID, - confirmationDialog = true + confirmationDialog = true, ) ), listOf( @@ -580,7 +584,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataAccountRecommendationOneAlert: SafetyCenterData @@ -593,7 +597,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataDeviceRecommendationOneAlert: SafetyCenterData @@ -606,7 +610,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) ) ), - emptyList() + emptyList(), ) private val safetyCenterDataGeneralCriticalOneAlert: SafetyCenterData @@ -615,7 +619,7 @@ class SafetyCenterManagerTest { safetyCenterStatusGeneralCriticalOneAlert, listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataAccountCriticalOneAlert: SafetyCenterData @@ -624,7 +628,7 @@ class SafetyCenterManagerTest { safetyCenterStatusAccountCriticalOneAlert, listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataDeviceCriticalOneAlert: SafetyCenterData @@ -633,7 +637,7 @@ class SafetyCenterManagerTest { safetyCenterStatusDeviceCriticalOneAlert, listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataCriticalOneAlertInFlight: SafetyCenterData @@ -643,11 +647,11 @@ class SafetyCenterManagerTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( SINGLE_SOURCE_ID, - isActionInFlight = true + isActionInFlight = true, ) ), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) private val safetyCenterDataOkReviewOneDismissedAlertInFlight: SafetyCenterData @@ -656,13 +660,13 @@ class SafetyCenterManagerTest { safetyCenterStatusOkReview, emptyList(), listOf(safetyCenterEntryOrGroupCritical), - emptyList() + emptyList(), ) .withDismissedIssuesIfAtLeastU( listOf( safetyCenterTestData.safetyCenterIssueCritical( SINGLE_SOURCE_ID, - isActionInFlight = true + isActionInFlight = true, ) ) ) @@ -698,17 +702,17 @@ class SafetyCenterManagerTest { .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) .setPendingIntent(null) .setEnabled(false) - .build() + .build(), ) ) .build() ), - safetyCenterEntryGroupMixedFromComplexConfig + safetyCenterEntryGroupMixedFromComplexConfig, ), listOf( safetyCenterStaticEntryGroupFromComplexConfig, - safetyCenterStaticEntryGroupMixedFromComplexConfig - ) + safetyCenterStaticEntryGroupMixedFromComplexConfig, + ), ) private val safetyCenterDataFromComplexConfigUpdated: SafetyCenterData @@ -718,28 +722,28 @@ class SafetyCenterManagerTest { listOf( safetyCenterTestData.safetyCenterIssueCritical( DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueCritical( ISSUE_ONLY_BAREBONE_ID, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID + groupId = DYNAMIC_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, - groupId = ISSUE_ONLY_GROUP_ID + groupId = ISSUE_ONLY_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + groupId = MIXED_STATELESS_GROUP_ID, ), safetyCenterTestData.safetyCenterIssueInformation( ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID - ) + groupId = MIXED_STATELESS_GROUP_ID, + ), ), listOf( SafetyCenterEntryOrGroup( @@ -761,7 +765,7 @@ class SafetyCenterManagerTest { ), safetyCenterTestData.safetyCenterEntryUnspecified( DYNAMIC_HIDDEN_ID, - pendingIntent = null + pendingIntent = null, ), safetyCenterTestData.safetyCenterEntryOk( DYNAMIC_HIDDEN_WITH_SEARCH_ID @@ -770,21 +774,22 @@ class SafetyCenterManagerTest { .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) .setPendingIntent(null) .setEnabled(false) - .build() + .build(), ) ) .build() ), - safetyCenterEntryGroupMixedFromComplexConfig + safetyCenterEntryGroupMixedFromComplexConfig, ), listOf( safetyCenterStaticEntryGroupFromComplexConfig, - safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig - ) + safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig, + ), ) - @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) - @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) + @get:Rule(order = 1) val flagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @get:Rule(order = 2) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 3) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) @Test fun getSafetySourceData_differentPackageWithManageSafetyCenterPermission_returnsData() { @@ -833,7 +838,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_RESCAN_BUTTON_CLICK @@ -864,7 +869,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) val listener = safetyCenterTestHelper.addListener() @@ -889,7 +894,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val listener = safetyCenterTestHelper.addListener() @@ -913,7 +918,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) val listener = safetyCenterTestHelper.addListener() @@ -939,7 +944,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -957,13 +962,13 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) assertFailsWith(TimeoutCancellationException::class) { safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_PAGE_OPEN, - timeout = TIMEOUT_SHORT + timeout = TIMEOUT_SHORT, ) } val apiSafetySourceDataBeforeSettingFlag = @@ -985,7 +990,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1015,7 +1020,7 @@ class SafetyCenterManagerTest { val expectedExplicitPendingIntent = SafetySourceTestData.createRedirectPendingIntent( context, - Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName) + Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName), ) val defaultEntryPendingIntent = apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent @@ -1035,7 +1040,7 @@ class SafetyCenterManagerTest { val expectedImplicitPendingIntent = SafetySourceTestData.createRedirectPendingIntent( context, - Intent(ACTION_TEST_ACTIVITY_EXPORTED) + Intent(ACTION_TEST_ACTIVITY_EXPORTED), ) val defaultEntryPendingIntent = apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent @@ -1055,7 +1060,7 @@ class SafetyCenterManagerTest { val expectedImplicitPendingIntent = SafetySourceTestData.createRedirectPendingIntent( context, - Intent(ACTION_TEST_ACTIVITY_EXPORTED) + Intent(ACTION_TEST_ACTIVITY_EXPORTED), ) val staticEntryPendingIntent = apiSafetyCenterData.staticEntryGroups @@ -1081,7 +1086,7 @@ class SafetyCenterManagerTest { val expectedExplicitPendingIntent = SafetySourceTestData.createRedirectPendingIntent( context, - Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName) + Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName), ) val staticEntryPendingIntent = apiSafetyCenterData.staticEntryGroups @@ -1141,7 +1146,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithIconAction + safetySourceTestData.informationWithIconAction, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1155,7 +1160,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithIssueWithAttributionTitle + safetySourceTestData.informationWithIssueWithAttributionTitle, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1184,7 +1189,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceNoGroupTitleConfig) safetyCenterTestHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1195,11 +1200,11 @@ class SafetyCenterManagerTest { listOf( safetyCenterTestData.safetyCenterIssueRecommendation( ISSUE_ONLY_ALL_OPTIONAL_ID, - attributionTitle = null + attributionTitle = null, ) ), emptyList(), - emptyList() + emptyList(), ) assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData) } @@ -1222,7 +1227,7 @@ class SafetyCenterManagerTest { val previousApiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1236,7 +1241,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1259,7 +1264,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithInformationIssue + safetySourceTestData.criticalWithInformationIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1272,7 +1277,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1286,7 +1291,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithIssueWithActionConfirmation + safetySourceTestData.recommendationWithIssueWithActionConfirmation, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1300,7 +1305,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1313,7 +1318,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithDeviceIssue + safetySourceTestData.recommendationWithDeviceIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1326,7 +1331,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1339,7 +1344,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingAccountIssue + safetySourceTestData.criticalWithResolvingAccountIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1352,7 +1357,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingDeviceIssue + safetySourceTestData.criticalWithResolvingDeviceIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1371,7 +1376,7 @@ class SafetyCenterManagerTest { .defaultRecommendationIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1380,7 +1385,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_data_recommendation_title", - OVERALL_SEVERITY_LEVEL_RECOMMENDATION + OVERALL_SEVERITY_LEVEL_RECOMMENDATION, ) ) } @@ -1396,7 +1401,7 @@ class SafetyCenterManagerTest { .defaultCriticalResolvingIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1405,7 +1410,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_critical_data_warning_title", - OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, ) ) } @@ -1421,7 +1426,7 @@ class SafetyCenterManagerTest { .defaultRecommendationIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1430,7 +1435,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_passwords_recommendation_title", - OVERALL_SEVERITY_LEVEL_RECOMMENDATION + OVERALL_SEVERITY_LEVEL_RECOMMENDATION, ) ) } @@ -1446,7 +1451,7 @@ class SafetyCenterManagerTest { .defaultCriticalResolvingIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1455,7 +1460,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_critical_passwords_warning_title", - OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, ) ) } @@ -1471,7 +1476,7 @@ class SafetyCenterManagerTest { .defaultRecommendationIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1480,7 +1485,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_personal_recommendation_title", - OVERALL_SEVERITY_LEVEL_RECOMMENDATION + OVERALL_SEVERITY_LEVEL_RECOMMENDATION, ) ) } @@ -1496,7 +1501,7 @@ class SafetyCenterManagerTest { .defaultCriticalResolvingIssueBuilder() .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1505,7 +1510,7 @@ class SafetyCenterManagerTest { .isEqualTo( safetyCenterTestData.safetyCenterStatusOneAlert( "overall_severity_level_critical_personal_warning_title", - OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING + OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, ) ) } @@ -1521,7 +1526,7 @@ class SafetyCenterManagerTest { .defaultInformationIssueBuilder() .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1557,7 +1562,7 @@ class SafetyCenterManagerTest { .defaultInformationIssueBuilder("id_5") .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP) .build(), - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1577,7 +1582,7 @@ class SafetyCenterManagerTest { .defaultInformationIssueBuilder() .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) .build() - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1613,7 +1618,7 @@ class SafetyCenterManagerTest { .defaultInformationIssueBuilder("id_5") .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL) .build(), - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1632,8 +1637,8 @@ class SafetyCenterManagerTest { safetySourceTestData .defaultInformationIssueBuilder() .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL) - .build(), - ) + .build() + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1643,7 +1648,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterStatusNAlerts( "overall_severity_level_ok_title", OVERALL_SEVERITY_LEVEL_OK, - numAlerts = 1 + numAlerts = 1, ) ) } @@ -1671,7 +1676,7 @@ class SafetyCenterManagerTest { .defaultInformationIssueBuilder("id_4") .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC) .build(), - ) + ), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1681,7 +1686,7 @@ class SafetyCenterManagerTest { safetyCenterTestData.safetyCenterStatusNAlerts( "overall_severity_level_ok_title", OVERALL_SEVERITY_LEVEL_OK, - numAlerts = 2 + numAlerts = 2, ) ) } @@ -1696,7 +1701,7 @@ class SafetyCenterManagerTest { assertThat( SafetyCenterBundles.getStaticEntryId( apiSafetyCenterData, - apiSafetyCenterData.staticEntryGroups[0].staticEntries[0] + apiSafetyCenterData.staticEntryGroups[0].staticEntries[0], ) ) .isEqualTo( @@ -1708,7 +1713,7 @@ class SafetyCenterManagerTest { assertThat( SafetyCenterBundles.getStaticEntryId( apiSafetyCenterData, - apiSafetyCenterData.staticEntryGroups[1].staticEntries[0] + apiSafetyCenterData.staticEntryGroups[1].staticEntries[0], ) ) .isEqualTo( @@ -1724,7 +1729,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingDeviceIssueAndRecommendationIssue + safetySourceTestData.criticalWithResolvingDeviceIssueAndRecommendationIssue, ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1737,11 +1742,11 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_3, - safetySourceTestData.criticalWithResolvingDeviceIssue + safetySourceTestData.criticalWithResolvingDeviceIssue, ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1754,11 +1759,11 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_3, - safetySourceTestData.criticalWithResolvingDeviceIssue + safetySourceTestData.criticalWithResolvingDeviceIssue, ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -1777,14 +1782,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -1793,7 +1798,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) } @@ -1809,14 +1814,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 and source group MULTIPLE_SOURCES_GROUP_ID_2 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1839,14 +1844,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 and source group MULTIPLE_SOURCES_GROUP_ID_1 safetyCenterTestHelper.setData( SOURCE_ID_2, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1869,14 +1874,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 and source group MULTIPLE_SOURCES_GROUP_ID_2 safetyCenterTestHelper.setData( SOURCE_ID_6, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -1897,14 +1902,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("different") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -1913,12 +1918,12 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ), safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 - ) + groupId = MULTIPLE_SOURCES_GROUP_ID_2, + ), ) .inOrder() } @@ -1934,14 +1939,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_6, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -1950,12 +1955,12 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ), safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_6, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 - ) + groupId = MULTIPLE_SOURCES_GROUP_ID_2, + ), ) .inOrder() } @@ -1971,21 +1976,21 @@ class SafetyCenterManagerTest { SOURCE_ID_4, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_6, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_7, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -1994,7 +1999,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_4, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) } @@ -2010,14 +2015,14 @@ class SafetyCenterManagerTest { SOURCE_ID_2, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -2026,7 +2031,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) } @@ -2042,49 +2047,49 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("A") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_2, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("A") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_2 safetyCenterTestHelper.setData( SOURCE_ID_3, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("B") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_4, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("B") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("A") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_6, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("B") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_3 safetyCenterTestHelper.setData( SOURCE_ID_7, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("B") - ) + ), ) val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues @@ -2093,16 +2098,16 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ), safetyCenterTestData.safetyCenterIssueRecommendation( SOURCE_ID_3, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ), safetyCenterTestData.safetyCenterIssueRecommendation( SOURCE_ID_4, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 - ) + groupId = MULTIPLE_SOURCES_GROUP_ID_1, + ), ) .inOrder() } @@ -2118,14 +2123,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2143,7 +2148,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) } @@ -2159,14 +2164,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID) @@ -2180,7 +2185,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) assertThat(apiSafetyCenterDismissedIssues).isEmpty() @@ -2197,14 +2202,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2219,7 +2224,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) } @@ -2235,14 +2240,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_5, CRITICAL_ISSUE_ID) @@ -2257,7 +2262,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) } @@ -2277,14 +2282,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2298,7 +2303,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { @@ -2309,7 +2314,7 @@ class SafetyCenterManagerTest { .contains( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) hasResurfaced @@ -2324,13 +2329,13 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 99L, - SEVERITY_LEVEL_CRITICAL_WARNING to 0L + 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 + SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO, ) safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -2340,14 +2345,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2361,7 +2366,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueRecommendation( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { @@ -2372,7 +2377,7 @@ class SafetyCenterManagerTest { .contains( safetyCenterTestData.safetyCenterIssueRecommendation( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) hasResurfaced @@ -2386,13 +2391,13 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 99L, - SEVERITY_LEVEL_CRITICAL_WARNING 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) + SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY.multipliedBy(100), ) safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -2402,14 +2407,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2422,7 +2427,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) assertFailsWith(TimeoutCancellationException::class) { @@ -2434,7 +2439,7 @@ class SafetyCenterManagerTest { .contains( safetyCenterTestData.safetyCenterIssueRecommendation( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) hasResurfaced @@ -2449,13 +2454,13 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 99L, - SEVERITY_LEVEL_CRITICAL_WARNING 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 + SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY, ) safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -2465,14 +2470,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID) @@ -2485,7 +2490,7 @@ class SafetyCenterManagerTest { .containsExactly( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) { @@ -2496,7 +2501,7 @@ class SafetyCenterManagerTest { .contains( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_1, - groupId = MULTIPLE_SOURCES_GROUP_ID_1 + groupId = MULTIPLE_SOURCES_GROUP_ID_1, ) ) hasResurfaced @@ -2515,14 +2520,14 @@ class SafetyCenterManagerTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) // Belongs to DEDUPLICATION_GROUP_1 safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) val listener = safetyCenterTestHelper.addListener() @@ -2540,7 +2545,7 @@ class SafetyCenterManagerTest { .contains( safetyCenterTestData.safetyCenterIssueCritical( SOURCE_ID_5, - groupId = MULTIPLE_SOURCES_GROUP_ID_2 + groupId = MULTIPLE_SOURCES_GROUP_ID_2, ) ) hasResurfaced @@ -2566,7 +2571,7 @@ class SafetyCenterManagerTest { .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE) .build() ) - .build() + .build(), ) val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status @@ -2579,32 +2584,32 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig) safetyCenterTestHelper.setData( DYNAMIC_BAREBONE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterTestHelper.setData( DYNAMIC_DISABLED_ID, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, safetySourceTestData.unspecified) safetyCenterTestHelper.setData( DYNAMIC_HIDDEN_WITH_SEARCH_ID, - safetySourceTestData.information + safetySourceTestData.information, ) safetyCenterTestHelper.setData( ISSUE_ONLY_BAREBONE_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.criticalResolvingGeneralIssue), ) safetyCenterTestHelper.setData( ISSUE_ONLY_ALL_OPTIONAL_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), ) safetyCenterTestHelper.setData( DYNAMIC_IN_STATELESS_ID, - safetySourceTestData.unspecifiedWithIssue + safetySourceTestData.unspecifiedWithIssue, ) safetyCenterTestHelper.setData( ISSUE_ONLY_IN_STATELESS_ID, - SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -2618,50 +2623,50 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig) safetyCenterManager.reportSafetySourceErrorWithPermission( SOURCE_ID_1, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) safetyCenterTestHelper.setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified" - ) + entrySummary = "unspecified", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues" - ) + entrySummary = "information without issues", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_RECOMMENDATION, - entrySummary = "recommendation" - ) + entrySummary = "recommendation", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING, - entrySummary = "critical 1" - ) + entrySummary = "critical 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_7, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING, - entrySummary = "critical 2" - ) + entrySummary = "critical 2", + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2677,43 +2682,43 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig) safetyCenterManager.reportSafetySourceErrorWithPermission( SOURCE_ID_1, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) safetyCenterTestHelper.setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified" - ) + entrySummary = "unspecified", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues" - ) + entrySummary = "information without issues", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_RECOMMENDATION, - entrySummary = "recommendation 1" - ) + entrySummary = "recommendation 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_RECOMMENDATION, - entrySummary = "recommendation 2" - ) + entrySummary = "recommendation 2", + ), ) // SOURCE_ID_7 leave as an UNKNOWN dynamic entry // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2732,52 +2737,52 @@ class SafetyCenterManagerTest { SOURCE_ID_1, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 1" - ) + entrySummary = "unspecified 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 2" - ) + entrySummary = "unspecified 2", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues 1" - ) + entrySummary = "information without issues 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue 1", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue 2", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues 2" - ) + entrySummary = "information without issues 2", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_7, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 3" - ) + entrySummary = "unspecified 3", + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2795,50 +2800,50 @@ class SafetyCenterManagerTest { SOURCE_ID_1, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 1" - ) + entrySummary = "unspecified 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 2" - ) + entrySummary = "unspecified 2", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues 1" - ) + entrySummary = "information without issues 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues 2" - ) + entrySummary = "information without issues 2", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 3" - ) + entrySummary = "unspecified 3", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information without issues 3" - ) + entrySummary = "information without issues 3", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_7, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 4" - ) + entrySummary = "unspecified 4", + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2856,51 +2861,51 @@ class SafetyCenterManagerTest { SOURCE_ID_1, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 1" - ) + entrySummary = "unspecified 1", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 2" - ) + entrySummary = "unspecified 2", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 3" - ) + entrySummary = "unspecified 3", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 4" - ) + entrySummary = "unspecified 4", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 6" - ) + entrySummary = "unspecified 6", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_7, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified 7" - ) + entrySummary = "unspecified 7", + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2916,34 +2921,34 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig) safetyCenterManager.reportSafetySourceErrorWithPermission( SOURCE_ID_1, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) safetyCenterManager.reportSafetySourceErrorWithPermission( SOURCE_ID_2, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) safetyCenterTestHelper.setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified" - ) + entrySummary = "unspecified", + ), ) // SOURCE_ID_5 leave as an UNKNOWN dynamic entry safetyCenterTestHelper.setData( SOURCE_ID_6, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information" - ) + entrySummary = "information", + ), ) // SOURCE_ID_7 leave as an UNKNOWN dynamic entry // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -2960,7 +2965,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.summaryTestConfig) safetyCenterManager.reportSafetySourceErrorWithPermission( SOURCE_ID_1, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) // SOURCE_ID_2 leave as an UNKNOWN dynamic entry safetyCenterTestHelper.setData( @@ -2968,15 +2973,15 @@ class SafetyCenterManagerTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified" - ) + entrySummary = "unspecified", + ), ) // SOURCE_ID_5 leave as an UNKNOWN dynamic entry // SOURCE_ID_6 leave as an UNKNOWN dynamic entry @@ -2984,8 +2989,8 @@ class SafetyCenterManagerTest { SOURCE_ID_7, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information" - ) + entrySummary = "information", + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -3006,22 +3011,22 @@ class SafetyCenterManagerTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information with issue", - withIssue = true - ) + withIssue = true, + ), ) safetyCenterTestHelper.setData( SOURCE_ID_4, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, - entrySummary = "unspecified" - ) + entrySummary = "unspecified", + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_INFORMATION, - entrySummary = "information" - ) + entrySummary = "information", + ), ) // SOURCE_ID_6 leave as an UNKNOWN dynamic entry safetyCenterTestHelper.setData( @@ -3029,8 +3034,8 @@ class SafetyCenterManagerTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified with issue", - withIssue = true - ) + withIssue = true, + ), ) // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry @@ -3078,7 +3083,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData( DYNAMIC_DISABLED_ID, - safetySourceTestData.informationWithIssue + safetySourceTestData.informationWithIssue, ) val informationWithIssueGroup = @@ -3118,7 +3123,7 @@ class SafetyCenterManagerTest { .build() ) ), - emptyList() + emptyList(), ) assertThat(safetyCenterData).isEqualTo(expectedSafetyCenterData) } @@ -3128,7 +3133,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceInvalidIntentConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithNullIntent + safetySourceTestData.informationWithNullIntent, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() @@ -3147,12 +3152,111 @@ class SafetyCenterManagerTest { .build() ) ), - emptyList() + emptyList(), ) assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData) } @Test + @RequiresFlagsEnabled(Flags.FLAG_SAFETY_CENTER_ISSUE_ONLY_AFFECTS_GROUP_STATUS) + fun getSafetyCenterData_entryGroupWithIssueOnlySourceHighestOfGroup_affectsEntryGroupStatus() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig) + safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information) + safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) + safetyCenterTestHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.severityLevel) + .isEqualTo(ENTRY_SEVERITY_LEVEL_RECOMMENDATION) + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.summary!!.toString()) + .isEqualTo("Recommendation issue title") + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_SAFETY_CENTER_ISSUE_ONLY_AFFECTS_GROUP_STATUS) + fun getSafetyCenterData_entryGroupWithIssueOnlySourceSameAsGroup_affectsTitle() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig) + safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information) + safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) + safetyCenterTestHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.severityLevel) + .isEqualTo(ENTRY_SEVERITY_LEVEL_OK) + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.summary!!.toString()) + .isEqualTo("Information issue title") + } + + @Test + fun getSafetyCenterData_entryGroupWithIssueOnlySourceNotHighest_doesntAffectEntryGroupStatus() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig) + safetyCenterTestHelper.setData( + SOURCE_ID_1, + safetySourceTestData.recommendationWithGeneralIssue, + ) + safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) + safetyCenterTestHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue), + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.severityLevel) + .isEqualTo(ENTRY_SEVERITY_LEVEL_RECOMMENDATION) + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.summary!!.toString()) + .isEqualTo("Recommendation summary") + } + + @Test + fun getSafetyCenterData_entryGroupWithIssueOnlySourceDismissed_doesntAffectEntryGroupStatus() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig) + safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information) + safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) + safetyCenterTestHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), + ) + safetyCenterManager.dismissSafetyCenterIssueWithPermission( + SafetyCenterTestData.issueId(ISSUE_ONLY_ALL_OPTIONAL_ID, RECOMMENDATION_ISSUE_ID) + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.severityLevel) + .isEqualTo(ENTRY_SEVERITY_LEVEL_OK) + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.summary!!.toString()) + .isEqualTo("OK") + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_SAFETY_CENTER_ISSUE_ONLY_AFFECTS_GROUP_STATUS) + fun getSafetyCenterData_entryGroupWithIssueOnlySourceFlagOff_doesntAffectEntryGroupStatus() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.entryGroupWithIssueOnlyConfig) + safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information) + safetyCenterTestHelper.setData(SOURCE_ID_2, safetySourceTestData.information) + safetyCenterTestHelper.setData( + ISSUE_ONLY_ALL_OPTIONAL_ID, + SafetySourceTestData.issuesOnly(safetySourceTestData.recommendationGeneralIssue), + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.severityLevel) + .isEqualTo(ENTRY_SEVERITY_LEVEL_OK) + assertThat(apiSafetyCenterData.entriesOrGroups.single().entryGroup!!.summary!!.toString()) + .isEqualTo("OK") + } + + @Test fun addOnSafetyCenterDataChangedListener_listenerCalledWithSafetyCenterDataFromConfig() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) @@ -3183,7 +3287,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val safetyCenterDataFromListener = listener.receiveSafetyCenterData() @@ -3209,7 +3313,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() @@ -3230,7 +3334,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID) @@ -3246,7 +3350,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val safetyCenterDataAfterSourcePushesDismissedIssueAgain = @@ -3260,14 +3364,14 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - issueTypeId = "some_other_issue_type_id" + issueTypeId = "some_other_issue_type_id", ) ) @@ -3284,11 +3388,11 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationDismissPendingIntentIssue + safetySourceTestData.recommendationDismissPendingIntentIssue, ) SafetySourceReceiver.setResponse( Request.DismissIssue(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val listener = safetyCenterTestHelper.addListener() @@ -3362,13 +3466,13 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 99L, - SEVERITY_LEVEL_CRITICAL_WARNING to 99L + SEVERITY_LEVEL_CRITICAL_WARNING to 99L, ) SafetyCenterFlags.resurfaceIssueDelays = mapOf( SEVERITY_LEVEL_INFORMATION to Duration.ZERO, SEVERITY_LEVEL_RECOMMENDATION to Duration.ZERO, - SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO + SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO, ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) @@ -3399,18 +3503,18 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 0L, - SEVERITY_LEVEL_CRITICAL_WARNING to 2L + SEVERITY_LEVEL_CRITICAL_WARNING to 2L, ) SafetyCenterFlags.resurfaceIssueDelays = mapOf( SEVERITY_LEVEL_INFORMATION to RESURFACE_DELAY, SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY, - SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO + SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO, ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingDeviceIssue + safetySourceTestData.criticalWithResolvingDeviceIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() checkState(apiSafetyCenterData == safetyCenterDataDeviceCriticalOneAlert) @@ -3455,18 +3559,18 @@ class SafetyCenterManagerTest { mapOf( SEVERITY_LEVEL_INFORMATION to 0L, SEVERITY_LEVEL_RECOMMENDATION to 99L, - SEVERITY_LEVEL_CRITICAL_WARNING to 0L + 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 + SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO, ) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithDeviceIssue + safetySourceTestData.recommendationWithDeviceIssue, ) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() checkState(apiSafetyCenterData == safetyCenterDataDeviceRecommendationOneAlert) @@ -3494,12 +3598,12 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -3507,8 +3611,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData() @@ -3523,7 +3627,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() SafetySourceReceiver.setResponse(Request.ResolveAction(SINGLE_SOURCE_ID), Response.Error) @@ -3533,8 +3637,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData() @@ -3558,7 +3662,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() @@ -3567,8 +3671,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData() @@ -3592,7 +3696,7 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -3600,8 +3704,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) listener.receiveSafetyCenterData() listener.receiveSafetyCenterData() @@ -3609,7 +3713,7 @@ class SafetyCenterManagerTest { SafetyCenterFlags.resolveActionTimeout = TIMEOUT_LONG SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -3617,8 +3721,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData() @@ -3633,11 +3737,11 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val issueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID) val listener = safetyCenterTestHelper.addListener() @@ -3650,8 +3754,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) if (SdkLevel.isAtLeastU()) { @@ -3671,12 +3775,12 @@ class SafetyCenterManagerTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) val listener = safetyCenterTestHelper.addListener() SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) assertFailsWith(IllegalArgumentException::class) { safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( @@ -3684,9 +3788,9 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID + "invalid", - CRITICAL_ISSUE_ACTION_ID + CRITICAL_ISSUE_ACTION_ID, ), - TIMEOUT_SHORT + TIMEOUT_SHORT, ) } @@ -3695,8 +3799,8 @@ class SafetyCenterManagerTest { SafetyCenterTestData.issueActionId( SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, - CRITICAL_ISSUE_ACTION_ID - ) + CRITICAL_ISSUE_ACTION_ID, + ), ) val safetyCenterDataFromListenerDuringResolveAction = listener.receiveSafetyCenterData() @@ -3787,7 +3891,7 @@ class SafetyCenterManagerTest { safetyCenterManager.reportSafetySourceErrorWithPermission( SINGLE_SOURCE_ID, - SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED), ) val lastUpdated = dumpLastUpdated() @@ -3836,9 +3940,11 @@ class SafetyCenterManagerTest { companion object { private val RESURFACE_DELAY = Duration.ofMillis(500) + // Wait 3 times the RESURFACE_DELAY before asserting whether an issue has or has not // resurfaced. Use a constant additive error buffer if we increase the delay considerably. private val RESURFACE_TIMEOUT = RESURFACE_DELAY.multipliedBy(3) + // Check more than once during a RESURFACE_DELAY before asserting whether an issue has or // has not resurfaced. Use a different check logic (focused at the expected resurface time) // if we increase the delay considerably. diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt index 1678ccced..ce75b00f5 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt @@ -116,7 +116,7 @@ class SafetyCenterNotificationTest { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) TestNotificationListener.waitForZeroNotifications() @@ -128,7 +128,7 @@ class SafetyCenterNotificationTest { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) TestNotificationListener.waitForZeroNotifications() @@ -140,7 +140,7 @@ class SafetyCenterNotificationTest { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) TestNotificationListener.waitForZeroNotifications() @@ -401,7 +401,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) .build() ) @@ -440,7 +440,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) .build() ) @@ -497,7 +497,7 @@ class SafetyCenterNotificationTest { "New action", safetySourceTestData.createTestActivityRedirectPendingIntent( identifier = "new_action" - ) + ), ) .build() ) @@ -697,13 +697,13 @@ class SafetyCenterNotificationTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) TestNotificationListener.waitForNotificationsMatching( @@ -728,7 +728,7 @@ class SafetyCenterNotificationTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) val notificationWithChannel = @@ -745,7 +745,7 @@ class SafetyCenterNotificationTest { SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) TestNotificationListener.waitForNotificationsMatching( @@ -762,7 +762,7 @@ class SafetyCenterNotificationTest { fun setSafetySourceData_withInformationIssue_lowImportanceBlockableNotification() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.informationWithIssue + safetySourceTestData.informationWithIssue, ) TestNotificationListener.waitForNotificationsMatching( @@ -781,7 +781,7 @@ class SafetyCenterNotificationTest { fun setSafetySourceData_withRecommendationIssue_defaultImportanceUnblockableNotification() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) TestNotificationListener.waitForNotificationsMatching( @@ -800,7 +800,7 @@ class SafetyCenterNotificationTest { fun setSafetySourceData_withCriticalIssue_highImportanceUnblockableNotification() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.criticalWithResolvingDeviceIssue(sourceId = uniqueSafetySourceId) + safetySourceTestData.criticalWithResolvingDeviceIssue(sourceId = uniqueSafetySourceId), ) TestNotificationListener.waitForNotificationsMatching( @@ -819,7 +819,7 @@ class SafetyCenterNotificationTest { fun dismissSafetyCenterIssue_dismissesNotification() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithAccountIssue + safetySourceTestData.recommendationWithAccountIssue, ) TestNotificationListener.waitForSingleNotificationMatching( @@ -832,7 +832,7 @@ class SafetyCenterNotificationTest { safetyCenterManager.dismissSafetyCenterIssueWithPermission( SafetyCenterTestData.issueId( uniqueSafetySourceId, - SafetySourceTestData.RECOMMENDATION_ISSUE_ID + SafetySourceTestData.RECOMMENDATION_ISSUE_ID, ) ) @@ -843,7 +843,7 @@ class SafetyCenterNotificationTest { fun dismissingNotification_doesNotUpdateSafetyCenterData() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId) + safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId), ) // Add the listener after setting the initial data so that we don't need to consume/receive // an update for that @@ -876,13 +876,13 @@ class SafetyCenterNotificationTest { SOURCE_ID_1, SafetySourceTestData.issuesOnly( safetySourceTestData.criticalIssueWithDeduplicationId("same") - ) + ), ) safetyCenterTestHelper.setData( SOURCE_ID_5, SafetySourceTestData.issuesOnly( safetySourceTestData.recommendationIssueWithDeduplicationId("same") - ) + ), ) val notificationWithChannel = @@ -926,7 +926,7 @@ class SafetyCenterNotificationTest { fun sendActionPendingIntent_successful_updatesListener() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId) + safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -940,7 +940,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) val listener = safetyCenterTestHelper.addListener() @@ -958,7 +958,7 @@ class SafetyCenterNotificationTest { fun sendActionPendingIntent_successfulNoSuccessMessage_removesNotification() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId) + safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -972,7 +972,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) sendActionPendingIntentAndWaitWithPermission(action) @@ -986,7 +986,7 @@ class SafetyCenterNotificationTest { uniqueSafetySourceId, safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage( sourceId = uniqueSafetySourceId - ) + ), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1000,7 +1000,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) sendActionPendingIntentAndWaitWithPermission(action) @@ -1017,7 +1017,7 @@ class SafetyCenterNotificationTest { uniqueSafetySourceId, safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage( sourceId = uniqueSafetySourceId - ) + ), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1031,7 +1031,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) sendActionPendingIntentAndWaitWithPermission(action) @@ -1049,7 +1049,7 @@ class SafetyCenterNotificationTest { uniqueSafetySourceId, safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage( sourceId = uniqueSafetySourceId - ) + ), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1063,7 +1063,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) SafetyCenterFlags.notificationsEnabled = false @@ -1078,7 +1078,7 @@ class SafetyCenterNotificationTest { uniqueSafetySourceId, safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage( sourceId = uniqueSafetySourceId - ) + ), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1095,8 +1095,8 @@ class SafetyCenterNotificationTest { Response.SetData( safetySourceTestData.information, overrideSafetyEvent = - SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() - ) + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(), + ), ) sendActionPendingIntentAndWaitWithPermission(action) @@ -1118,8 +1118,8 @@ class SafetyCenterNotificationTest { "Solve now!", safetySourceTestData.resolvingActionPendingIntent( sourceId = uniqueSafetySourceId, - sourceIssueActionId = "notification_action_id" - ) + sourceIssueActionId = "notification_action_id", + ), ) .setWillResolve(true) .setSuccessMessage("Solved via notification action :)") @@ -1140,8 +1140,8 @@ class SafetyCenterNotificationTest { "Default action", safetySourceTestData.resolvingActionPendingIntent( sourceId = uniqueSafetySourceId, - sourceIssueActionId = "issue_action_id" - ) + sourceIssueActionId = "issue_action_id", + ), ) .setWillResolve(true) .setSuccessMessage("Solved via issue action :(") @@ -1168,7 +1168,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) sendActionPendingIntentAndWaitWithPermission(action) @@ -1185,7 +1185,7 @@ class SafetyCenterNotificationTest { // to resolve that action successfully. safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId) + safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = uniqueSafetySourceId), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1199,7 +1199,7 @@ class SafetyCenterNotificationTest { checkNotNull(action) { "Notification action unexpectedly null" } SafetySourceReceiver.setResponse( Request.ResolveAction(uniqueSafetySourceId), - Response.Error + Response.Error, ) val listener = safetyCenterTestHelper.addListener() @@ -1224,7 +1224,7 @@ class SafetyCenterNotificationTest { fun sendContentPendingIntent_singleIssue_opensSafetyCenterWithIssueVisible() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithDeviceIssue + safetySourceTestData.recommendationWithDeviceIssue, ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1245,11 +1245,11 @@ class SafetyCenterNotificationTest { setFlagsForImmediateNotifications(SOURCE_ID_1) safetyCenterTestHelper.setData( SOURCE_ID_1, - safetySourceTestData.recommendationWithDeviceIssue + safetySourceTestData.recommendationWithDeviceIssue, ) safetyCenterTestHelper.setData( SOURCE_ID_2, - safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = SOURCE_ID_2) + safetySourceTestData.criticalWithResolvingGeneralIssue(sourceId = SOURCE_ID_2), ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1269,7 +1269,7 @@ class SafetyCenterNotificationTest { fun whenGreenIssue_notificationHasAutoCancel() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.informationWithIssue + safetySourceTestData.informationWithIssue, ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1286,7 +1286,7 @@ class SafetyCenterNotificationTest { fun whenNotGreenIssue_notificationDoesntHaveAutoCancel() { safetyCenterTestHelper.setData( uniqueSafetySourceId, - safetySourceTestData.recommendationWithDeviceIssue + safetySourceTestData.recommendationWithDeviceIssue, ) val notificationWithChannel = TestNotificationListener.waitForSingleNotificationMatching( @@ -1305,7 +1305,7 @@ class SafetyCenterNotificationTest { fun sendActionPendingIntentAndWaitWithPermission( action: Notification.Action, - timeout: Duration = Coroutines.TIMEOUT_LONG + timeout: Duration = Coroutines.TIMEOUT_LONG, ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { PendingIntentSender.send(action.actionIntent) @@ -1328,13 +1328,13 @@ class SafetyCenterNotificationTest { fun sendContentPendingIntent( statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - andExecuteBlock: () -> Unit = {} + andExecuteBlock: () -> Unit = {}, ) { val contentIntent = statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent executeBlockAndExit( launchActivity = { PendingIntentSender.send(contentIntent) }, - block = andExecuteBlock + block = andExecuteBlock, ) } } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt index 4ba293eb9..5cafe3d48 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt @@ -105,7 +105,7 @@ class SafetySourceDataFixesTest { .addAction( safetySourceTestData.action( id = targetActionId, - pendingIntent = originalPendingIntent + pendingIntent = originalPendingIntent, ) ) .build() @@ -126,6 +126,7 @@ class SafetySourceDataFixesTest { ) assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue() } + @Test @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun defaultActionOverride_notification_overridesMatchingActions() { @@ -143,7 +144,7 @@ class SafetySourceDataFixesTest { notification( safetySourceTestData.action( id = targetActionId, - pendingIntent = originalPendingIntent + pendingIntent = originalPendingIntent, ) ) ) @@ -183,7 +184,7 @@ class SafetySourceDataFixesTest { .addAction( safetySourceTestData.action( id = targetActionId, - pendingIntent = originalPendingIntent + pendingIntent = originalPendingIntent, ) ) .build() @@ -192,7 +193,7 @@ class SafetySourceDataFixesTest { safetyCenterTestHelper.setData( SOURCE_ID_2, // Different source ID - dataWithoutActionToOverride + dataWithoutActionToOverride, ) val actualPendingIntent = @@ -219,7 +220,7 @@ class SafetySourceDataFixesTest { .addAction( safetySourceTestData.action( id = "DifferentActionId", - pendingIntent = originalPendingIntent + pendingIntent = originalPendingIntent, ) ) .build() @@ -253,7 +254,7 @@ class SafetySourceDataFixesTest { .addAction( safetySourceTestData.action( id = targetActionId, - pendingIntent = originalPendingIntent + pendingIntent = originalPendingIntent, ) ) .build() @@ -289,7 +290,7 @@ class SafetySourceDataFixesTest { private fun intentsFilterEqual( actualPendingIntent: PendingIntent, - expectedPendingIntent: PendingIntent? + expectedPendingIntent: PendingIntent?, ) = callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { actualPendingIntent.intentFilterEquals(expectedPendingIntent) diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt index f76a52256..6f147dfbf 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt @@ -69,7 +69,7 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("scanning_title"), - safetyCenterResourcesApk.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("loading_summary"), ) } } @@ -79,13 +79,13 @@ class SafetyCenterStatusCardTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.informationWithIconAction + safetySourceTestData.informationWithIconAction, ) context.launchSafetyCenterActivity { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesApk.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("loading_summary"), ) } } @@ -98,7 +98,7 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_title"), - safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary"), ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) } @@ -110,13 +110,13 @@ class SafetyCenterStatusCardTest { preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"), ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) } @@ -128,7 +128,7 @@ class SafetyCenterStatusCardTest { preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -143,13 +143,13 @@ class SafetyCenterStatusCardTest { preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.informationWithIssue) + Response.SetData(safetySourceTestData.informationWithIssue), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) waitButtonNotDisplayed(RESCAN_BUTTON_LABEL) } @@ -160,11 +160,11 @@ class SafetyCenterStatusCardTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.recommendationWithGeneralIssue + safetySourceTestData.recommendationWithGeneralIssue, ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.recommendationWithGeneralIssue) + Response.SetData(safetySourceTestData.recommendationWithGeneralIssue), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -172,7 +172,7 @@ class SafetyCenterStatusCardTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_safety_recommendation_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) waitButtonNotDisplayed(RESCAN_BUTTON_LABEL) } @@ -183,11 +183,11 @@ class SafetyCenterStatusCardTest { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, - safetySourceTestData.criticalWithResolvingGeneralIssue + safetySourceTestData.criticalWithResolvingGeneralIssue, ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) + Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -195,7 +195,7 @@ class SafetyCenterStatusCardTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_safety_warning_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) waitButtonNotDisplayed(RESCAN_BUTTON_LABEL) } @@ -207,20 +207,20 @@ class SafetyCenterStatusCardTest { preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"), ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() } waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("scanning_title"), - safetyCenterResourcesApk.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("loading_summary"), ) } } @@ -231,17 +231,17 @@ class SafetyCenterStatusCardTest { preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.information) + Response.SetData(safetySourceTestData.information), ) SafetySourceReceiver.setResponse( Request.Rescan(SINGLE_SOURCE_ID), - Response.SetData(safetySourceTestData.recommendationWithGeneralIssue) + Response.SetData(safetySourceTestData.recommendationWithGeneralIssue), ) context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary"), ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() } @@ -250,7 +250,7 @@ class SafetyCenterStatusCardTest { safetyCenterResourcesApk.getStringByName( "overall_severity_level_safety_recommendation_title" ), - safetyCenterTestData.getAlertString(1) + safetyCenterTestData.getAlertString(1), ) } } diff --git a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt index 7ad83b949..c3e104504 100644 --- a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt +++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt @@ -47,9 +47,9 @@ import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed import com.android.safetycenter.testing.UiTestHelper.waitAllTextNotDisplayed import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed import com.android.safetycenter.testing.UiTestHelper.waitDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed +import java.util.regex.Pattern import org.junit.After import org.junit.Before import org.junit.Rule @@ -98,7 +98,7 @@ class PrivacySubpageTest { context.getString(firstSource.summaryResId), "Controls", context.getString(lastSource.titleResId), - context.getString(lastSource.summaryResId) + context.getString(lastSource.summaryResId), ) } } @@ -117,7 +117,7 @@ class PrivacySubpageTest { waitButtonDisplayed("Exit test activity") { it.click() } waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) } } @@ -156,16 +156,16 @@ class PrivacySubpageTest { context.launchSafetyCenterActivity(extras) { waitAllText( displayed = sensorPrivacyManager.supportsSensorToggle(CAMERA), - text = "Camera access" + text = "Camera access", ) waitAllText( displayed = sensorPrivacyManager.supportsSensorToggle(MICROPHONE), - text = "Microphone access" + text = "Microphone access", ) waitAllTextDisplayed("Show clipboard access") waitAllText( displayed = getPermissionControllerBool("config_display_show_password_toggle"), - text = "Show passwords" + text = "Show passwords", ) waitAllTextDisplayed("Location access") } @@ -179,16 +179,14 @@ class PrivacySubpageTest { val source: SafetySource = sourcesGroup.safetySources.first() val extras = Bundle() extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id) + val containsLocationPattern = Pattern.compile(".*[Ll]ocation.*") // NOTYPO context.launchSafetyCenterActivity(extras) { - openPageAndExit("Location access") { - waitPageTitleDisplayed("Location") - waitAllTextDisplayed("Use location") - } + openPageAndExit("Location access") { waitDisplayed(By.text(containsLocationPattern)) } waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) } } @@ -225,7 +223,7 @@ class PrivacySubpageTest { permissionControllerContext.resources.getIdentifier( resourceName, "bool", - "com.android.permissioncontroller" + "com.android.permissioncontroller", ) return permissionControllerContext.resources.getBoolean(resourceId) } diff --git a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt index 236beb34e..5c770cc92 100644 --- a/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt +++ b/tests/functional/safetycenter/subpages/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt @@ -160,7 +160,7 @@ class SafetyCenterSubpagesTest { context.getString(firstGroup.titleResId), context.getString(firstGroup.summaryResId), context.getString(lastGroup.titleResId), - context.getString(lastGroup.summaryResId) + context.getString(lastGroup.summaryResId), ) openPageAndExit(context.getString(lastGroup.titleResId)) { @@ -191,7 +191,7 @@ class SafetyCenterSubpagesTest { waitAllTextDisplayed( context.getString(firstGroup.titleResId), context.getString(firstGroup.summaryResId), - context.getString(lastGroup.titleResId) + context.getString(lastGroup.titleResId), ) waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() } @@ -232,24 +232,24 @@ class SafetyCenterSubpagesTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_1_TITLE, - entrySummary = SAFETY_SOURCE_1_SUMMARY - ) + entrySummary = SAFETY_SOURCE_1_SUMMARY, + ), ) setData( SOURCE_ID_2, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_2_TITLE, - entrySummary = SAFETY_SOURCE_2_SUMMARY - ) + entrySummary = SAFETY_SOURCE_2_SUMMARY, + ), ) setData( SOURCE_ID_3, safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, entryTitle = SAFETY_SOURCE_3_TITLE, - entrySummary = SAFETY_SOURCE_3_SUMMARY - ) + entrySummary = SAFETY_SOURCE_3_SUMMARY, + ), ) } val firstGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[0] @@ -263,7 +263,7 @@ class SafetyCenterSubpagesTest { SAFETY_SOURCE_1_TITLE, SAFETY_SOURCE_1_SUMMARY, SAFETY_SOURCE_2_TITLE, - SAFETY_SOURCE_2_SUMMARY + SAFETY_SOURCE_2_SUMMARY, ) } @@ -287,7 +287,7 @@ class SafetyCenterSubpagesTest { waitButtonDisplayed("Exit test activity") { it.click() } waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) } } @@ -370,7 +370,7 @@ class SafetyCenterSubpagesTest { openPageAndExit(context.getString(sourcesGroup.titleResId)) { waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) } @@ -380,15 +380,15 @@ class SafetyCenterSubpagesTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION, entryTitle = "Updated title", - entrySummary = "Updated summary" + entrySummary = "Updated summary", ) - ) + ), ) openPageAndExit(context.getString(sourcesGroup.titleResId)) { waitAllTextNotDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) waitAllTextDisplayed("Updated title", "Updated summary") } @@ -405,7 +405,7 @@ class SafetyCenterSubpagesTest { openPageAndExit(context.getString(sourcesGroup.titleResId)) { waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) SafetySourceReceiver.setResponse( @@ -414,15 +414,15 @@ class SafetyCenterSubpagesTest { safetySourceTestData.buildSafetySourceDataWithSummary( severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION, entryTitle = "Updated title", - entrySummary = "Updated summary" + entrySummary = "Updated summary", ) - ) + ), ) UiAutomatorUtils2.getUiDevice().rotate() waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) waitAllTextNotDisplayed("Updated title", "Updated summary") } @@ -489,7 +489,7 @@ class SafetyCenterSubpagesTest { // Clear the data when action is triggered to simulate resolution. SafetySourceReceiver.setResponse( Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData + Response.ClearData, ) context.launchSafetyCenterActivity(withReceiverPermission = true) { @@ -921,7 +921,7 @@ class SafetyCenterSubpagesTest { waitPageTitleDisplayed(context.getString(sourcesGroup.titleResId)) waitAllTextDisplayed( context.getString(source.titleResId), - context.getString(source.summaryResId) + context.getString(source.summaryResId), ) } } @@ -952,7 +952,7 @@ class SafetyCenterSubpagesTest { waitAllTextDisplayed( context.getString(source.titleResId), context.getString(source.summaryResId), - safetyCenterResourcesApk.getStringByName("test_single_source_group_id_footer") + safetyCenterResourcesApk.getStringByName("test_single_source_group_id_footer"), ) } } @@ -975,7 +975,7 @@ class SafetyCenterSubpagesTest { private fun checkOnDismissedIssue( sourcesGroup: SafetySourcesGroup, issue: SafetySourceIssue, - block: () -> Unit + block: () -> Unit, ) { val safetyCenterIssueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, issue.id) safetyCenterTestHelper.dismissSafetyCenterIssue(safetyCenterIssueId) diff --git a/tests/hostside/safetycenter/Android.bp b/tests/hostside/safetycenter/Android.bp index 10258f95b..931dfb719 100644 --- a/tests/hostside/safetycenter/Android.bp +++ b/tests/hostside/safetycenter/Android.bp @@ -33,7 +33,7 @@ java_test_host { static_libs: [ "cts-statsd-atom-host-test-utils", ], - data: [":SafetyCenterHostSideTestsHelper"], + device_common_data: [":SafetyCenterHostSideTestsHelper"], test_suites: [ "general-tests", "mts-permission", diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt index a7009b19e..cc8c53d5e 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt @@ -56,14 +56,14 @@ object Coroutines { /** Shorthand for [runBlocking] combined with [withTimeoutOrNull] */ fun <T> runBlockingWithTimeoutOrNull( timeout: Duration = TIMEOUT_LONG, - block: suspend () -> T + block: suspend () -> T, ): T? = runBlocking { withTimeoutOrNull(timeout.toMillis()) { block() } } /** Check a condition using coroutines with a timeout. */ fun waitForWithTimeout( timeout: Duration = TIMEOUT_LONG, checkPeriod: Duration = CHECK_PERIOD, - condition: () -> Boolean + condition: () -> Boolean, ) { runBlockingWithTimeout(timeout) { waitFor(checkPeriod, condition) } } @@ -72,7 +72,7 @@ object Coroutines { fun waitForSuccessWithTimeout( timeout: Duration = TIMEOUT_LONG, checkPeriod: Duration = CHECK_PERIOD, - fallibleAction: () -> Unit + fallibleAction: () -> Unit, ) { waitForWithTimeout(timeout, checkPeriod) { try { diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt index 1ed0ecbc3..7303d631b 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EnableSensorRule.kt @@ -42,7 +42,7 @@ class EnableSensorRule(context: Context, val sensor: Int) : TestRule { override fun evaluate() { assumeTrue( "Test device does not support toggling sensor $sensor", - supportsSensorToggle() + supportsSensorToggle(), ) val oldSensorPrivacy = isSensorPrivacyEnabled() setSensorPrivacy(false) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt index 2dedfc853..77577d504 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/EqualsHashCodeToStringTester.kt @@ -45,7 +45,7 @@ private constructor( private val ignoreHashCode: Boolean = false, private val ignoreToString: Boolean = false, private val parcelRoundTripEqualsEquivalence: Equivalence<T>? = null, - private val createCopy: ((T) -> T)? = null + private val createCopy: ((T) -> T)? = null, ) { private val equalsTester = EqualsTester() @@ -85,13 +85,13 @@ private constructor( parcelableCreator: Parcelable.Creator<T>, ignoreHashCode: Boolean = false, ignoreToString: Boolean = false, - createCopy: ((T) -> T)? = null + createCopy: ((T) -> T)? = null, ): EqualsHashCodeToStringTester<T> = EqualsHashCodeToStringTester( ignoreHashCode, ignoreToString, parcelRoundTripEqualsEquivalence(parcelableCreator), - createCopy + createCopy, ) /** @@ -103,13 +103,13 @@ private constructor( fun <T> of( ignoreHashCode: Boolean = false, ignoreToString: Boolean = false, - createCopy: ((T) -> T)? = null + createCopy: ((T) -> T)? = null, ): EqualsHashCodeToStringTester<T> = EqualsHashCodeToStringTester( ignoreHashCode, ignoreToString, parcelRoundTripEqualsEquivalence = null, - createCopy + createCopy, ) /** diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt index 81b752bca..11827a5f4 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/NotificationCharacteristics.kt @@ -38,7 +38,7 @@ data class NotificationCharacteristics( private fun importanceMatches( statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristicImportance: Int + characteristicImportance: Int, ): Boolean { return characteristicImportance == IMPORTANCE_ANY || statusBarNotificationWithChannel.channel.importance == characteristicImportance @@ -46,7 +46,7 @@ data class NotificationCharacteristics( private fun blockableMatches( statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristicBlockable: Boolean? + characteristicBlockable: Boolean?, ): Boolean { return characteristicBlockable == null || statusBarNotificationWithChannel.channel.isBlockable == characteristicBlockable @@ -54,7 +54,7 @@ data class NotificationCharacteristics( fun safetySourceIdMatches( statusBarNotification: StatusBarNotification, - safetySourceId: String? + safetySourceId: String?, ): Boolean { return safetySourceId == null || SafetyCenterIds.issueKeyFromString(statusBarNotification.tag).safetySourceId == @@ -63,7 +63,7 @@ data class NotificationCharacteristics( private fun isMatch( statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristic: NotificationCharacteristics + characteristic: NotificationCharacteristics, ): Boolean { val notif = statusBarNotificationWithChannel.statusBarNotification.notification val extras = notif.extras @@ -75,13 +75,13 @@ data class NotificationCharacteristics( blockableMatches(statusBarNotificationWithChannel, characteristic.blockable) && safetySourceIdMatches( statusBarNotificationWithChannel.statusBarNotification, - characteristic.safetySourceId + characteristic.safetySourceId, ) } fun areMatching( statusBarNotifications: List<StatusBarNotificationWithChannel>, - characteristics: List<NotificationCharacteristics> + characteristics: List<NotificationCharacteristics>, ): Boolean { if (statusBarNotifications.size != characteristics.size) { return false diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt index 40515fa33..62f324d89 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterActivityLauncher.kt @@ -48,13 +48,13 @@ object SafetyCenterActivityLauncher { intentAction: String = ACTION_SAFETY_CENTER, withReceiverPermission: Boolean = false, preventTrampolineToSettings: Boolean = true, - block: () -> Unit + block: () -> Unit, ) { val launchSafetyCenterIntent = createIntent( intentAction, intentExtras, - preventTrampolineToSettings = preventTrampolineToSettings + preventTrampolineToSettings = preventTrampolineToSettings, ) if (withReceiverPermission) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { @@ -83,7 +83,7 @@ object SafetyCenterActivityLauncher { private fun createIntent( intentAction: String, intentExtras: Bundle?, - preventTrampolineToSettings: Boolean = false + preventTrampolineToSettings: Boolean = false, ): Intent { val launchIntent = Intent(intentAction).addFlags(FLAG_ACTIVITY_NEW_TASK).addFlags(FLAG_ACTIVITY_CLEAR_TASK) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt index 961d03c47..b5f732bad 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt @@ -52,7 +52,7 @@ object SafetyCenterApisWithShellPermissions { fun SafetyCenterManager.setSafetySourceDataWithPermission( safetySourceId: String, safetySourceData: SafetySourceData?, - safetyEvent: SafetyEvent + safetyEvent: SafetyEvent, ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { setSafetySourceData(safetySourceId, safetySourceData, safetyEvent) @@ -72,7 +72,7 @@ object SafetyCenterApisWithShellPermissions { */ fun SafetyCenterManager.reportSafetySourceErrorWithPermission( safetySourceId: String, - safetySourceErrorDetails: SafetySourceErrorDetails + safetySourceErrorDetails: SafetySourceErrorDetails, ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { reportSafetySourceError(safetySourceId, safetySourceErrorDetails) @@ -85,7 +85,7 @@ object SafetyCenterApisWithShellPermissions { */ fun SafetyCenterManager.refreshSafetySourcesWithPermission( refreshReason: Int, - safetySourceIds: List<String>? = null + safetySourceIds: List<String>? = null, ) { callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) { if (safetySourceIds != null) { @@ -116,7 +116,7 @@ object SafetyCenterApisWithShellPermissions { */ fun SafetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( executor: Executor, - listener: OnSafetyCenterDataChangedListener + listener: OnSafetyCenterDataChangedListener, ) { callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) { addOnSafetyCenterDataChangedListener(executor, listener) @@ -151,7 +151,7 @@ object SafetyCenterApisWithShellPermissions { */ fun SafetyCenterManager.executeSafetyCenterIssueActionWithPermission( safetyCenterIssueId: String, - safetyCenterIssueActionId: String + safetyCenterIssueActionId: String, ) { callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) { executeSafetyCenterIssueAction(safetyCenterIssueId, safetyCenterIssueActionId) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt index f8926caac..e454b6e25 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterEnabledChangedReceiver.kt @@ -54,16 +54,14 @@ class SafetyCenterEnabledChangedReceiver(private val context: Context) : Broadca fun setSafetyCenterEnabledWithReceiverPermissionAndWait( value: Boolean, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ): Boolean = callWithShellPermissionIdentity(READ_SAFETY_CENTER_STATUS) { SafetyCenterFlags.isEnabled = value receiveSafetyCenterEnabledChanged(timeout) } - fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait( - value: Boolean, - ) { + fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value: Boolean) { SafetyCenterFlags.isEnabled = value WaitForBroadcasts.waitForBroadcasts() receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt index 912ea44ad..26a66a1a3 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt @@ -17,6 +17,7 @@ package com.android.safetycenter.testing import android.Manifest.permission.READ_DEVICE_CONFIG +import android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG import android.Manifest.permission.WRITE_DEVICE_CONFIG import android.annotation.TargetApi import android.app.job.JobInfo @@ -61,7 +62,7 @@ object SafetyCenterFlags { Flag( "safety_center_notifications_min_delay", defaultValue = Duration.ofHours(2), - DurationParser() + DurationParser(), ) /** @@ -72,7 +73,7 @@ object SafetyCenterFlags { Flag( "safety_center_notifications_allowed_sources", defaultValue = emptySet(), - SetParser(StringParser()) + SetParser(StringParser()), ) /** @@ -83,7 +84,7 @@ object SafetyCenterFlags { Flag( "safety_center_notifications_immediate_behavior_issues", defaultValue = emptySet(), - SetParser(StringParser()) + SetParser(StringParser()), ) /** @@ -98,7 +99,7 @@ object SafetyCenterFlags { Flag( "safety_center_notification_resurface_interval", defaultValue = Duration.ofDays(-1), - DurationParser() + DurationParser(), ) /** Flag that determines whether we should replace the IconAction of the lock screen source. */ @@ -119,7 +120,7 @@ object SafetyCenterFlags { Flag( "safety_center_refresh_sources_timeouts_millis", defaultValue = getAllRefreshTimeoutsMap(TEST_TIMEOUT), - MapParser(IntParser(), DurationParser()) + MapParser(IntParser(), DurationParser()), ) /** @@ -130,7 +131,7 @@ object SafetyCenterFlags { Flag( "safety_center_resolve_action_timeout_millis", defaultValue = TIMEOUT_LONG, - DurationParser() + DurationParser(), ) /** Flag that determines a duration after which a temporarily hidden issue will resurface. */ @@ -138,7 +139,7 @@ object SafetyCenterFlags { Flag( "safety_center_temp_hidden_issue_resurface_delay_millis", defaultValue = Duration.ofDays(2), - DurationParser() + DurationParser(), ) /** @@ -149,7 +150,7 @@ object SafetyCenterFlags { Flag( "safety_center_hide_resolved_ui_transition_delay_millis", defaultValue = Duration.ofMillis(400), - DurationParser() + DurationParser(), ) /** @@ -161,7 +162,7 @@ object SafetyCenterFlags { Flag( "safety_center_untracked_sources", defaultValue = emptySet(), - SetParser(StringParser()) + SetParser(StringParser()), ) /** @@ -173,7 +174,7 @@ object SafetyCenterFlags { Flag( "safety_center_resurface_issue_max_counts", defaultValue = emptyMap(), - MapParser(IntParser(), LongParser()) + MapParser(IntParser(), LongParser()), ) /** @@ -187,7 +188,7 @@ object SafetyCenterFlags { Flag( "safety_center_resurface_issue_delays_millis", defaultValue = emptyMap(), - MapParser(IntParser(), DurationParser()) + MapParser(IntParser(), DurationParser()), ) /** @@ -199,7 +200,7 @@ object SafetyCenterFlags { Flag( "safety_center_issue_category_allowlists", defaultValue = emptyMap(), - MapParser(IntParser(), SetParser(StringParser(), delimiter = "|")) + MapParser(IntParser(), SetParser(StringParser(), delimiter = "|")), ) /** @@ -211,7 +212,7 @@ object SafetyCenterFlags { Flag( "safety_center_actions_to_override_with_default_intent", defaultValue = emptyMap(), - MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")) + MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")), ) /** @@ -223,7 +224,7 @@ object SafetyCenterFlags { Flag( "safety_center_background_refresh_denied_sources", defaultValue = emptySet(), - SetParser(StringParser()) + SetParser(StringParser()), ) /** @@ -245,7 +246,7 @@ object SafetyCenterFlags { Flag( "safety_center_qs_tile_component_setting_flags", defaultValue = PackageManager.DONT_KILL_APP, - IntParser() + IntParser(), ) /** @@ -259,7 +260,7 @@ object SafetyCenterFlags { Flag( "safety_center_override_refresh_on_page_open_sources", defaultValue = setOf(), - SetParser(StringParser()) + SetParser(StringParser()), ) /** @@ -272,7 +273,7 @@ object SafetyCenterFlags { // do not set defaultValue to true, do not want background refreshes running // during other tests defaultValue = false, - BooleanParser() + BooleanParser(), ) /** @@ -286,7 +287,7 @@ object SafetyCenterFlags { Flag( "safety_center_periodic_background_interval_millis", defaultValue = Duration.ofDays(1), - DurationParser() + DurationParser(), ) /** Flag for allowlisting additional certificates for a given package. */ @@ -294,7 +295,7 @@ object SafetyCenterFlags { Flag( "safety_center_additional_allow_package_certs", defaultValue = emptyMap(), - MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")) + MapParser(StringParser(), SetParser(StringParser(), delimiter = "|")), ) /** Every Safety Center flag. */ @@ -323,7 +324,7 @@ object SafetyCenterFlags { showSubpagesFlag, overrideRefreshOnPageOpenSourcesFlag, backgroundRefreshIsEnabledFlag, - periodicBackgroundRefreshIntervalFlag + periodicBackgroundRefreshIntervalFlag, ) /** A property that allows getting and setting the [isEnabledFlag]. */ @@ -453,7 +454,7 @@ object SafetyCenterFlags { REFRESH_REASON_DEVICE_LOCALE_CHANGE to refreshTimeout, REFRESH_REASON_SAFETY_CENTER_ENABLED to refreshTimeout, REFRESH_REASON_OTHER to refreshTimeout, - REFRESH_REASON_PERIODIC to refreshTimeout + REFRESH_REASON_PERIODIC to refreshTimeout, ) private interface Parser<T> { @@ -486,7 +487,7 @@ object SafetyCenterFlags { private class SetParser<T>( private val elementParser: Parser<T>, - private val delimiter: String = "," + private val delimiter: String = ",", ) : Parser<Set<T>> { override fun parseFromString(stringValue: String) = stringValue.split(delimiter).map(elementParser::parseFromString).toSet() @@ -499,7 +500,7 @@ object SafetyCenterFlags { private val keyParser: Parser<K>, private val valueParser: Parser<V>, private val entriesDelimiter: String = ",", - private val pairDelimiter: String = ":" + private val pairDelimiter: String = ":", ) : Parser<Map<K, V>> { override fun parseFromString(stringValue: String) = stringValue.split(entriesDelimiter).associate { pair -> @@ -532,13 +533,13 @@ object SafetyCenterFlags { } private fun writeDeviceConfigProperty(name: String, stringValue: String?) { - callWithShellPermissionIdentity(WRITE_DEVICE_CONFIG) { + callWithShellPermissionIdentity(WRITE_DEVICE_CONFIG, WRITE_ALLOWLISTED_DEVICE_CONFIG) { val valueWasSet = DeviceConfig.setProperty( NAMESPACE_PRIVACY, name, stringValue, /* makeDefault */ - false + false, ) require(valueWasSet) { "Could not set $name to: $stringValue" } } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt index 0e31b2934..231cec821 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt @@ -48,7 +48,7 @@ class SafetyCenterTestConfigs(private val context: Context) { context.packageManager .getPackageInfo( context.packageName, - PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong()) + PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong()), ) .signingInfo!! .apkContentsSigners[0] @@ -220,25 +220,25 @@ class SafetyCenterTestConfigs(private val context: Context) { .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_1, - DEDUPLICATION_GROUP_1 + DEDUPLICATION_GROUP_1, ) ) .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_2, - DEDUPLICATION_GROUP_1 + DEDUPLICATION_GROUP_1, ) ) .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_3, - DEDUPLICATION_GROUP_2 + DEDUPLICATION_GROUP_2, ) ) .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_4, - DEDUPLICATION_GROUP_3 + DEDUPLICATION_GROUP_3, ) ) .build() @@ -248,13 +248,13 @@ class SafetyCenterTestConfigs(private val context: Context) { .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_5, - DEDUPLICATION_GROUP_1 + DEDUPLICATION_GROUP_1, ) ) .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_6, - DEDUPLICATION_GROUP_3 + DEDUPLICATION_GROUP_3, ) ) .build() @@ -264,7 +264,7 @@ class SafetyCenterTestConfigs(private val context: Context) { .addSafetySource( issueOnlySafetySourceWithDuplicationInfo( SOURCE_ID_7, - DEDUPLICATION_GROUP_3 + DEDUPLICATION_GROUP_3, ) ) .build() @@ -351,6 +351,23 @@ class SafetyCenterTestConfigs(private val context: Context) { .build() /** + * A simple [SafetyCenterConfig] for tests with a single stateful group containing 2 dynamic + * sources and an issue only source. + */ + val entryGroupWithIssueOnlyConfig = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + safetySourcesGroupBuilder("EntryGroupWithIssueOnly") + .addSafetySource(dynamicSource1) + .addSafetySource(dynamicSource2) + .addSafetySource( + issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build() + ) + .build() + ) + .build() + + /** * A simple [SafetyCenterConfig] for tests with multiple sources with one source having an * invalid default intent. */ diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt index 289bc32a8..631aae65f 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt @@ -80,7 +80,7 @@ class SafetyCenterTestData(context: Context) { ), safetyCenterResourcesApk.getStringByName( "overall_severity_level_ok_review_summary" - ) + ), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN) .build() @@ -91,7 +91,7 @@ class SafetyCenterTestData(context: Context) { */ fun safetyCenterStatusOneAlert( statusResource: String, - overallSeverityLevel: Int + overallSeverityLevel: Int, ): SafetyCenterStatus = safetyCenterStatusNAlerts(statusResource, overallSeverityLevel, 1) /** @@ -105,7 +105,7 @@ class SafetyCenterTestData(context: Context) { ): SafetyCenterStatus = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName(statusResource), - getAlertString(numAlerts) + getAlertString(numAlerts), ) .setSeverityLevel(overallSeverityLevel) .build() @@ -114,12 +114,10 @@ class SafetyCenterTestData(context: Context) { * Returns an information [SafetyCenterStatus] that has "Tip(s) available" as a summary for the * given [numTipIssues]. */ - fun safetyCenterStatusTips( - numTipIssues: Int, - ): SafetyCenterStatus = + fun safetyCenterStatusTips(numTipIssues: Int): SafetyCenterStatus = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), - getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues) + getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -128,15 +126,13 @@ class SafetyCenterTestData(context: Context) { * Returns an information [SafetyCenterStatus] that has "Action(s) taken" as a summary for the * given [numAutomaticIssues]. */ - fun safetyCenterStatusActionsTaken( - numAutomaticIssues: Int, - ): SafetyCenterStatus = + fun safetyCenterStatusActionsTaken(numAutomaticIssues: Int): SafetyCenterStatus = SafetyCenterStatus.Builder( safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), getIcuPluralsString( "overall_severity_level_action_taken_summary", - numAutomaticIssues - ) + numAutomaticIssues, + ), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) .build() @@ -150,7 +146,7 @@ class SafetyCenterTestData(context: Context) { safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_safety_warning_title" ), - getAlertString(numAlerts) + getAlertString(numAlerts), ) .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) .build() @@ -165,7 +161,7 @@ class SafetyCenterTestData(context: Context) { userId: Int = UserHandle.myUserId(), title: CharSequence = "OK", pendingIntent: PendingIntent? = - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) = SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) @@ -183,7 +179,7 @@ class SafetyCenterTestData(context: Context) { userId: Int = UserHandle.myUserId(), title: CharSequence = "OK", pendingIntent: PendingIntent? = - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) = safetyCenterEntryDefaultBuilder(sourceId, userId, title, pendingIntent).build() /** @@ -194,7 +190,7 @@ class SafetyCenterTestData(context: Context) { fun safetyCenterEntryDefaultStaticBuilder( sourceId: String, userId: Int = UserHandle.myUserId(), - title: CharSequence = "OK" + title: CharSequence = "OK", ) = SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) @@ -219,7 +215,7 @@ class SafetyCenterTestData(context: Context) { fun safetyCenterEntryUnspecified( sourceId: String, pendingIntent: PendingIntent? = - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) = SafetyCenterEntry.Builder(entryId(sourceId), "Unspecified title") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) @@ -237,7 +233,7 @@ class SafetyCenterTestData(context: Context) { fun safetyCenterEntryOkBuilder( sourceId: String, userId: Int = UserHandle.myUserId(), - title: CharSequence = "Ok title" + title: CharSequence = "Ok title", ) = SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_OK) @@ -252,7 +248,7 @@ class SafetyCenterTestData(context: Context) { fun safetyCenterEntryOk( sourceId: String, userId: Int = UserHandle.myUserId(), - title: CharSequence = "Ok title" + title: CharSequence = "Ok title", ) = safetyCenterEntryOkBuilder(sourceId, userId, title).build() /** @@ -262,7 +258,7 @@ class SafetyCenterTestData(context: Context) { */ fun safetyCenterEntryRecommendation( sourceId: String, - summary: String = "Recommendation summary" + summary: String = "Recommendation summary", ) = SafetyCenterEntry.Builder(entryId(sourceId), "Recommendation title") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_RECOMMENDATION) @@ -291,12 +287,12 @@ class SafetyCenterTestData(context: Context) { sourceId: String, userId: Int = UserHandle.myUserId(), attributionTitle: String? = "OK", - groupId: String? = SINGLE_SOURCE_GROUP_ID + groupId: String? = SINGLE_SOURCE_GROUP_ID, ) = SafetyCenterIssue.Builder( issueId(sourceId, INFORMATION_ISSUE_ID, userId = userId), "Information issue title", - "Information issue summary" + "Information issue summary", ) .setSeverityLevel(ISSUE_SEVERITY_LEVEL_OK) .setShouldConfirmDismissal(false) @@ -307,10 +303,10 @@ class SafetyCenterTestData(context: Context) { sourceId, INFORMATION_ISSUE_ID, INFORMATION_ISSUE_ACTION_ID, - userId + userId, ), "Review", - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) .build() ) @@ -332,12 +328,12 @@ class SafetyCenterTestData(context: Context) { userId: Int = UserHandle.myUserId(), attributionTitle: String? = "OK", groupId: String? = SINGLE_SOURCE_GROUP_ID, - confirmationDialog: Boolean = false + confirmationDialog: Boolean = false, ) = SafetyCenterIssue.Builder( issueId(sourceId, RECOMMENDATION_ISSUE_ID, userId = userId), "Recommendation issue title", - "Recommendation issue summary" + "Recommendation issue summary", ) .setSeverityLevel(ISSUE_SEVERITY_LEVEL_RECOMMENDATION) .setActions( @@ -347,10 +343,10 @@ class SafetyCenterTestData(context: Context) { sourceId, RECOMMENDATION_ISSUE_ID, RECOMMENDATION_ISSUE_ACTION_ID, - userId + userId, ), "See issue", - safetySourceTestData.createTestActivityRedirectPendingIntent() + safetySourceTestData.createTestActivityRedirectPendingIntent(), ) .apply { if (confirmationDialog && SdkLevel.isAtLeastU()) { @@ -359,7 +355,7 @@ class SafetyCenterTestData(context: Context) { "Confirmation title", "Confirmation text", "Confirmation yes", - "Confirmation no" + "Confirmation no", ) ) } @@ -384,12 +380,12 @@ class SafetyCenterTestData(context: Context) { isActionInFlight: Boolean = false, userId: Int = UserHandle.myUserId(), attributionTitle: String? = "OK", - groupId: String? = SINGLE_SOURCE_GROUP_ID + groupId: String? = SINGLE_SOURCE_GROUP_ID, ) = SafetyCenterIssue.Builder( issueId(sourceId, CRITICAL_ISSUE_ID, userId = userId), "Critical issue title", - "Critical issue summary" + "Critical issue summary", ) .setSeverityLevel(ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING) .setActions( @@ -399,10 +395,10 @@ class SafetyCenterTestData(context: Context) { sourceId, CRITICAL_ISSUE_ID, CRITICAL_ISSUE_ACTION_ID, - userId + userId, ), "Solve issue", - safetySourceTestData.criticalIssueActionPendingIntent + safetySourceTestData.criticalIssueActionPendingIntent, ) .setWillResolve(true) .setIsInFlight(isActionInFlight) @@ -432,7 +428,7 @@ class SafetyCenterTestData(context: Context) { val messageFormat = MessageFormat( safetyCenterResourcesApk.getStringByName(name, formatArgs), - Locale.getDefault() + Locale.getDefault(), ) val arguments = ArrayMap<String, Any>() arguments["count"] = count @@ -448,7 +444,7 @@ class SafetyCenterTestData(context: Context) { .build(), emptyList(), emptyList(), - emptyList() + emptyList(), ) /** Creates an ID for a Safety Center entry. */ @@ -465,7 +461,7 @@ class SafetyCenterTestData(context: Context) { sourceId: String, sourceIssueId: String, issueTypeId: String = ISSUE_TYPE_ID, - userId: Int = UserHandle.myUserId() + userId: Int = UserHandle.myUserId(), ) = SafetyCenterIds.encodeToString( SafetyCenterIssueId.newBuilder() @@ -485,7 +481,7 @@ class SafetyCenterTestData(context: Context) { sourceId: String, sourceIssueId: String, sourceIssueActionId: String, - userId: Int = UserHandle.myUserId() + userId: Int = UserHandle.myUserId(), ) = SafetyCenterIds.encodeToString( SafetyCenterIssueActionId.newBuilder() @@ -546,7 +542,7 @@ class SafetyCenterTestData(context: Context) { private fun SafetyCenterData.copy( issues: List<SafetyCenterIssue> = this.issues, dismissedIssues: List<SafetyCenterIssue> = this.dismissedIssues, - extras: Bundle = this.extras + extras: Bundle = this.extras, ): SafetyCenterData = SafetyCenterData.Builder(status) .apply { diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt index 2902cdd6a..ac646648a 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestHelper.kt @@ -16,12 +16,14 @@ package com.android.safetycenter.testing +import android.Manifest.permission.READ_DEVICE_CONFIG import android.Manifest.permission.READ_SAFETY_CENTER_STATUS import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE import android.content.Context import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.UserManager +import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.safetycenter.SafetyCenterManager import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceData @@ -29,6 +31,8 @@ import android.safetycenter.config.SafetyCenterConfig import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC import android.util.Log import androidx.annotation.RequiresApi +import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.addOnSafetyCenterDataChangedListenerWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.clearSafetyCenterConfigForTestsWithPermission @@ -42,6 +46,7 @@ import com.android.safetycenter.testing.SafetyCenterFlags.isSafetyCenterEnabled import com.android.safetycenter.testing.SafetySourceTestData.Companion.EVENT_SOURCE_STATE_CHANGED import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Assume.assumeTrue /** A class that facilitates settings up Safety Center in tests. */ @RequiresApi(TIRAMISU) @@ -61,13 +66,17 @@ class SafetyCenterTestHelper(val context: Context) { SafetySourceReceiver.setup() TestActivity.enableHighPriorityAlias() SafetyCenterFlags.setup() - setEnabled(true) + if (safetyCenterCanBeToggledUsingDeviceConfig()) { + setEnabled(true) + } } /** Resets the state of Safety Center. To be called after each test. */ fun reset() { Log.d(TAG, "reset") - setEnabled(true) + if (safetyCenterCanBeToggledUsingDeviceConfig()) { + setEnabled(true) + } listeners.forEach { safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(it) it.cancel() @@ -97,6 +106,10 @@ class SafetyCenterTestHelper(val context: Context) { Log.d(TAG, "isEnabled is already $value") return } + assumeTrue( + "Cannot toggle SafetyCenter using DeviceConfig", + safetyCenterCanBeToggledUsingDeviceConfig(), + ) setEnabledWaitingForSafetyCenterBroadcastIdle(value, safetyCenterConfig) } @@ -119,7 +132,7 @@ class SafetyCenterTestHelper(val context: Context) { val listener = SafetyCenterTestListener() safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( directExecutor(), - listener + listener, ) if (skipInitialData) { listener.receiveSafetyCenterData() @@ -132,14 +145,14 @@ class SafetyCenterTestHelper(val context: Context) { fun setData( safetySourceId: String, safetySourceData: SafetySourceData?, - safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED + safetyEvent: SafetyEvent = EVENT_SOURCE_STATE_CHANGED, ) { Log.d(TAG, "setData for $safetySourceId") require(isEnabled()) safetyCenterManager.setSafetySourceDataWithPermission( safetySourceId, safetySourceData, - safetyEvent + safetyEvent, ) } @@ -152,13 +165,15 @@ class SafetyCenterTestHelper(val context: Context) { } private fun resetFlags() { - setEnabled(SafetyCenterFlags.snapshot.isSafetyCenterEnabled()) + if (safetyCenterCanBeToggledUsingDeviceConfig()) { + setEnabled(SafetyCenterFlags.snapshot.isSafetyCenterEnabled()) + } SafetyCenterFlags.reset() } private fun setEnabledWaitingForSafetyCenterBroadcastIdle( value: Boolean, - safetyCenterConfig: SafetyCenterConfig + safetyCenterConfig: SafetyCenterConfig, ) = callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE, READ_SAFETY_CENTER_STATUS) { val enabledChangedReceiver = SafetyCenterEnabledChangedReceiver(context) @@ -202,7 +217,19 @@ class SafetyCenterTestHelper(val context: Context) { private fun isEnabled() = safetyCenterManager.isSafetyCenterEnabledWithPermission() - private companion object { - const val TAG: String = "SafetyCenterTestHelper" + companion object { + private const val TAG: String = "SafetyCenterTestHelper" + + /** Returns whether Safety Center can be enabled / disabled using a DeviceConfig flag. */ + fun safetyCenterCanBeToggledUsingDeviceConfig(): Boolean { + val deviceFlagsValueProvider = DeviceFlagsValueProvider() + val safetyCenterEnabledNoDeviceConfig = + callWithShellPermissionIdentity(READ_DEVICE_CONFIG) { + deviceFlagsValueProvider.getBoolean( + Flags.FLAG_SAFETY_CENTER_ENABLED_NO_DEVICE_CONFIG + ) + } + return !safetyCenterEnabledNoDeviceConfig || !SdkLevel.isAtLeastU() + } } } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt index 8ce5c25d4..46e9b6593 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt @@ -58,7 +58,7 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener { */ fun receiveSafetyCenterData( timeout: Duration = TIMEOUT_LONG, - matching: (SafetyCenterData) -> Boolean = { true } + matching: (SafetyCenterData) -> Boolean = { true }, ): SafetyCenterData = runBlockingWithTimeout(timeout) { var safetyCenterData = dataChannel.receive() @@ -78,7 +78,7 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener { */ fun waitForSafetyCenterRefresh( timeout: Duration = TIMEOUT_LONG, - withErrorEntry: Boolean? = null + withErrorEntry: Boolean? = null, ): SafetyCenterData { receiveSafetyCenterData(timeout) { it.status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS || diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt index dcbc4ebe9..c89b29b2a 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestRule.kt @@ -23,7 +23,7 @@ import org.junit.runners.model.Statement /** A JUnit [TestRule] that performs setup and reset steps before and after Safety Center tests. */ class SafetyCenterTestRule( private val safetyCenterTestHelper: SafetyCenterTestHelper, - private val withNotifications: Boolean = false + private val withNotifications: Boolean = false, ) : TestRule { override fun apply(base: Statement, description: Description): Statement { diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt index 8386228b8..77d338f90 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceIntentHandler.kt @@ -114,7 +114,7 @@ class SafetySourceIntentHandler { private suspend fun SafetyCenterManager.processRefreshSafetySources( intent: Intent, - userId: Int + userId: Int, ) { val broadcastId = intent.getStringExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID) if (broadcastId.isNullOrEmpty()) { @@ -190,7 +190,7 @@ class SafetySourceIntentHandler { private fun createResolveActionSuccessEvent( sourceIssueId: String, - sourceIssueActionId: String + sourceIssueActionId: String, ) = SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) .setSafetySourceIssueId(sourceIssueId) @@ -218,7 +218,7 @@ class SafetySourceIntentHandler { private suspend fun SafetyCenterManager.processRequest( request: Request, - safetyEventForResponse: (Response) -> SafetyEvent + safetyEventForResponse: (Response) -> SafetyEvent, ) { val response = mutex.withLock { requestsToResponses[request] } ?: return val safetyEvent = response.overrideSafetyEvent ?: safetyEventForResponse(response) @@ -242,25 +242,25 @@ class SafetySourceIntentHandler { /** Creates a refresh [Request] based on the given [sourceId] and [userId]. */ data class Refresh( override val sourceId: String, - override val userId: Int = UserHandle.myUserId() + override val userId: Int = UserHandle.myUserId(), ) : Request /** Creates a rescan [Request] based on the given [sourceId] and [userId]. */ data class Rescan( override val sourceId: String, - override val userId: Int = UserHandle.myUserId() + override val userId: Int = UserHandle.myUserId(), ) : Request /** Creates a resolve action [Request] based on the given [sourceId] and [userId]. */ data class ResolveAction( override val sourceId: String, - override val userId: Int = UserHandle.myUserId() + override val userId: Int = UserHandle.myUserId(), ) : Request /** Creates an issue dismissal [Request] based on the given [sourceId] and [userId]. */ data class DismissIssue( override val sourceId: String, - override val userId: Int = UserHandle.myUserId() + override val userId: Int = UserHandle.myUserId(), ) : Request } @@ -295,7 +295,7 @@ class SafetySourceIntentHandler { data class SetData( val safetySourceData: SafetySourceData, val overrideBroadcastId: String? = null, - override val overrideSafetyEvent: SafetyEvent? = null + override val overrideSafetyEvent: SafetyEvent? = null, ) : Response } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt index 29072c989..6b86df0dd 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceReceiver.kt @@ -86,7 +86,7 @@ class SafetySourceReceiver : BroadcastReceiver() { NotificationChannel( NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, - IMPORTANCE_DEFAULT + IMPORTANCE_DEFAULT, ) ) startForeground( @@ -98,7 +98,7 @@ class SafetySourceReceiver : BroadcastReceiver() { "ForegroundService" ) .setSmallIcon(android.R.drawable.ic_info) - .build() + .build(), ) serviceScope.launch { try { @@ -150,7 +150,7 @@ class SafetySourceReceiver : BroadcastReceiver() { componentName, if (enabled) COMPONENT_ENABLED_STATE_ENABLED else COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP + PackageManager.DONT_KILL_APP, ) } @@ -165,7 +165,7 @@ class SafetySourceReceiver : BroadcastReceiver() { fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( refreshReason: Int, safetySourceIds: List<String>? = null, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ): String = callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { refreshSafetySourcesWithPermission(refreshReason, safetySourceIds) @@ -174,7 +174,7 @@ class SafetySourceReceiver : BroadcastReceiver() { fun SafetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait( refreshReason: Int, - safetySourceIds: List<String>? = null + safetySourceIds: List<String>? = null, ) { refreshSafetySourcesWithPermission(refreshReason, safetySourceIds) WaitForBroadcasts.waitForBroadcasts() @@ -183,16 +183,14 @@ class SafetySourceReceiver : BroadcastReceiver() { fun setSafetyCenterEnabledWithReceiverPermissionAndWait( value: Boolean, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ): Boolean = callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { SafetyCenterFlags.isEnabled = value receiveSafetyCenterEnabledChanged(timeout) } - fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait( - value: Boolean, - ) { + fun setSafetyCenterEnabledWithoutReceiverPermissionAndWait(value: Boolean) { SafetyCenterFlags.isEnabled = value WaitForBroadcasts.waitForBroadcasts() receiveSafetyCenterEnabledChanged(TIMEOUT_SHORT) @@ -201,7 +199,7 @@ class SafetySourceReceiver : BroadcastReceiver() { fun SafetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait( issueId: String, issueActionId: String, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { executeSafetyCenterIssueActionWithPermission(issueId, issueActionId) @@ -211,7 +209,7 @@ class SafetySourceReceiver : BroadcastReceiver() { fun SafetyCenterManager.dismissSafetyCenterIssueWithPermissionAndWait( issueId: String, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { dismissSafetyCenterIssueWithPermission(issueId) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt index 7e77c0827..aad665004 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt @@ -63,11 +63,11 @@ class SafetySourceTestData(private val context: Context) { */ fun createTestActivityRedirectPendingIntent( explicit: Boolean = true, - identifier: String? = null + identifier: String? = null, ) = createRedirectPendingIntent( context, - createTestActivityIntent(context, explicit).setIdentifier(identifier) + createTestActivityIntent(context, explicit).setIdentifier(identifier), ) /** A [SafetySourceData] with a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus]. */ @@ -77,7 +77,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Unspecified title", "Unspecified summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setEnabled(false) .build() @@ -94,7 +94,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Clickable disabled title", "Clickable disabled summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setEnabled(false) .setPendingIntent(createTestActivityRedirectPendingIntent()) @@ -111,7 +111,7 @@ class SafetySourceTestData(private val context: Context) { fun defaultInformationIssueBuilder( id: String = INFORMATION_ISSUE_ID, title: String = "Information issue title", - summary: String = "Information issue summary" + summary: String = "Information issue summary", ) = SafetySourceIssue.Builder(id, title, summary, SEVERITY_LEVEL_INFORMATION, ISSUE_TYPE_ID) .addAction(action()) @@ -120,7 +120,7 @@ class SafetySourceTestData(private val context: Context) { fun action( id: String = INFORMATION_ISSUE_ACTION_ID, label: String = "Review", - pendingIntent: PendingIntent = createTestActivityRedirectPendingIntent() + pendingIntent: PendingIntent = createTestActivityRedirectPendingIntent(), ) = Action.Builder(id, label, pendingIntent).build() /** @@ -133,7 +133,7 @@ class SafetySourceTestData(private val context: Context) { "Information issue title", "Information issue summary", SEVERITY_LEVEL_INFORMATION, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .setSubtitle("Information issue subtitle") .addAction(action()) @@ -149,7 +149,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Unspecified title", "Unspecified summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -167,7 +167,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Unspecified title for Work", "Unspecified summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -185,7 +185,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Unspecified title for Private", "Unspecified summary", - SEVERITY_LEVEL_UNSPECIFIED + SEVERITY_LEVEL_UNSPECIFIED, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -292,7 +292,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Ok title for Work", "Ok summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -310,7 +310,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Ok title for Private", "Ok summary", - SEVERITY_LEVEL_INFORMATION + SEVERITY_LEVEL_INFORMATION, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -339,20 +339,20 @@ class SafetySourceTestData(private val context: Context) { fun defaultRecommendationIssueBuilder( title: String = "Recommendation issue title", summary: String = "Recommendation issue summary", - confirmationDialog: Boolean = false + confirmationDialog: Boolean = false, ) = SafetySourceIssue.Builder( RECOMMENDATION_ISSUE_ID, title, summary, SEVERITY_LEVEL_RECOMMENDATION, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction( Action.Builder( RECOMMENDATION_ISSUE_ACTION_ID, "See issue", - createTestActivityRedirectPendingIntent() + createTestActivityRedirectPendingIntent(), ) .apply { if (confirmationDialog && SdkLevel.isAtLeastU()) { @@ -418,7 +418,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Recommendation title", "Recommendation summary", - SEVERITY_LEVEL_RECOMMENDATION + SEVERITY_LEVEL_RECOMMENDATION, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -476,7 +476,7 @@ class SafetySourceTestData(private val context: Context) { fun resolvingActionPendingIntent( sourceId: String = SINGLE_SOURCE_ID, sourceIssueId: String = CRITICAL_ISSUE_ID, - sourceIssueActionId: String = CRITICAL_ISSUE_ACTION_ID + sourceIssueActionId: String = CRITICAL_ISSUE_ACTION_ID, ) = broadcastPendingIntent( Intent(ACTION_RESOLVE_ACTION) @@ -498,7 +498,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Solve issue", - criticalIssueActionPendingIntent(sourceId = sourceId) + criticalIssueActionPendingIntent(sourceId = sourceId), ) .setWillResolve(true) .build() @@ -510,7 +510,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Solve issue", - criticalIssueActionPendingIntent + criticalIssueActionPendingIntent, ) .setWillResolve(true) .setConfirmationDialogDetails(CONFIRMATION_DETAILS) @@ -521,7 +521,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Redirect", - createTestActivityRedirectPendingIntent() + createTestActivityRedirectPendingIntent(), ) .build() @@ -537,7 +537,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Solve issue", - criticalIssueActionPendingIntent(sourceId = sourceId) + criticalIssueActionPendingIntent(sourceId = sourceId), ) .setWillResolve(true) .setSuccessMessage("Issue solved") @@ -550,7 +550,7 @@ class SafetySourceTestData(private val context: Context) { "Critical issue title", "Critical issue summary", SEVERITY_LEVEL_CRITICAL_WARNING, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction(criticalResolvingActionWithSuccessMessage) .build() @@ -562,7 +562,7 @@ class SafetySourceTestData(private val context: Context) { "Critical issue title", "Critical issue summary", SEVERITY_LEVEL_CRITICAL_WARNING, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction(criticalResolvingActionWithSuccessMessage(sourceId = sourceId)) .build() @@ -577,13 +577,13 @@ class SafetySourceTestData(private val context: Context) { "Critical issue title 2", "Critical issue summary 2", SEVERITY_LEVEL_CRITICAL_WARNING, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction( Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Go solve issue", - createTestActivityRedirectPendingIntent() + createTestActivityRedirectPendingIntent(), ) .build() ) @@ -620,7 +620,7 @@ class SafetySourceTestData(private val context: Context) { "Critical issue title", "Critical issue summary", SEVERITY_LEVEL_CRITICAL_WARNING, - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction(criticalResolvingAction(sourceId)) @@ -679,7 +679,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Critical title", "Critical summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -811,7 +811,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Critical title", "Critical summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -829,7 +829,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Critical title", "Critical summary", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -854,7 +854,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceStatus.Builder( "Critical title 2", "Critical summary 2", - SEVERITY_LEVEL_CRITICAL_WARNING + SEVERITY_LEVEL_CRITICAL_WARNING, ) .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() @@ -870,7 +870,7 @@ class SafetySourceTestData(private val context: Context) { severityLevel: Int, entrySummary: String, withIssue: Boolean = false, - entryTitle: String = "Entry title" + entryTitle: String = "Entry title", ) = SafetySourceData.Builder() .setStatus( @@ -886,13 +886,13 @@ class SafetySourceTestData(private val context: Context) { "Issue title", "Issue summary", max(severityLevel, SEVERITY_LEVEL_INFORMATION), - ISSUE_TYPE_ID + ISSUE_TYPE_ID, ) .addAction( Action.Builder( "action_id", "Action", - createTestActivityRedirectPendingIntent() + createTestActivityRedirectPendingIntent(), ) .build() ) @@ -907,7 +907,7 @@ class SafetySourceTestData(private val context: Context) { context, 0, intent.addFlags(FLAG_RECEIVER_FOREGROUND).setPackage(context.packageName), - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) companion object { @@ -945,7 +945,7 @@ class SafetySourceTestData(private val context: Context) { CONFIRMATION_TITLE, CONFIRMATION_TEXT, CONFIRMATION_YES, - CONFIRMATION_NO + CONFIRMATION_NO, ) /** A [SafetyEvent] to push arbitrary changes to Safety Center. */ @@ -979,7 +979,7 @@ class SafetySourceTestData(private val context: Context) { context, 0 /* requestCode */, intent, - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, ) } } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt index 2ba127f4e..8fef70ce3 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SettingsPackage.kt @@ -38,7 +38,7 @@ object SettingsPackage { PackageManager.MATCH_DIRECT_BOOT_AWARE or PackageManager.MATCH_DIRECT_BOOT_UNAWARE) .toLong() - ) + ), )!! .activityInfo .packageName diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt index 53ea34362..a4569b72b 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/StatusBarNotificationWithChannel.kt @@ -22,5 +22,5 @@ import android.service.notification.StatusBarNotification /** Tuple of [StatusBarNotification] and the [NotificationChannel] it was posted to. */ data class StatusBarNotificationWithChannel( val statusBarNotification: StatusBarNotification, - val channel: NotificationChannel + val channel: NotificationChannel, ) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt index eceffb74f..12b4f7fd2 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestActivity.kt @@ -52,10 +52,12 @@ class TestActivity : Activity() { fun enableHighPriorityAlias() { setAliasEnabledState(COMPONENT_ENABLED_STATE_ENABLED) } + /** @see [enableHighPriorityAlias] */ fun disableHighPriorityAlias() { setAliasEnabledState(COMPONENT_ENABLED_STATE_DISABLED) } + private fun setAliasEnabledState(state: Int) { val name = ComponentName(getApplicationContext(), TestActivity::class.java.name + "Priority") diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt index 21bf76fad..17b520349 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/TestNotificationListener.kt @@ -115,7 +115,7 @@ class TestNotificationListener : NotificationListenerService() { */ fun waitForSingleNotificationMatching( characteristics: NotificationCharacteristics, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ): StatusBarNotificationWithChannel { return waitForNotificationsMatching(characteristics, timeout = timeout).first() } @@ -128,12 +128,12 @@ class TestNotificationListener : NotificationListenerService() { */ fun waitForNotificationsMatching( vararg characteristics: NotificationCharacteristics, - timeout: Duration = TIMEOUT_LONG + timeout: Duration = TIMEOUT_LONG, ): List<StatusBarNotificationWithChannel> { val charsList = characteristics.toList() return waitForNotificationsToSatisfy( timeout = timeout, - description = "notification(s) matching characteristics $charsList" + description = "notification(s) matching characteristics $charsList", ) { NotificationCharacteristics.areMatching(it, charsList) } @@ -147,7 +147,7 @@ class TestNotificationListener : NotificationListenerService() { */ fun waitForSuccessNotification( successMessage: String, - onNotification: (StatusBarNotification) -> Unit = {} + onNotification: (StatusBarNotification) -> Unit = {}, ) { // Only wait for the notification event and don't wait for all notifications to "settle" // as this notification is auto-cancelled after 10s; which can cause flakyness. @@ -185,7 +185,7 @@ class TestNotificationListener : NotificationListenerService() { timeout: Duration = TIMEOUT_LONG, forAtLeast: Duration = TIMEOUT_SHORT, description: String, - predicate: (List<StatusBarNotificationWithChannel>) -> Boolean + predicate: (List<StatusBarNotificationWithChannel>) -> Boolean, ): List<StatusBarNotificationWithChannel> { // First we wait at most timeout for the active notifications to satisfy the given // predicate or otherwise we throw: @@ -198,7 +198,7 @@ class TestNotificationListener : NotificationListenerService() { throw AssertionError( "Expected: $description, but notifications were " + "${getSafetyCenterNotifications()} after waiting for $timeout", - e + e, ) } @@ -298,7 +298,7 @@ class TestNotificationListener : NotificationListenerService() { getInstanceOrThrow().cancelNotification(key) waitForNotificationsToSatisfy( timeout = TIMEOUT_LONG, - description = "no notification with the key $key" + description = "no notification with the key $key", ) { notifications -> notifications.none { it.statusBarNotification.key == key } } @@ -327,7 +327,7 @@ class TestNotificationListener : NotificationListenerService() { throw IllegalStateException( "Notification dismissal was not recorded in the issue cache: " + dumpIssueDismissalsRepositoryState(), - e + e, ) } } @@ -362,7 +362,7 @@ class TestNotificationListener : NotificationListenerService() { fun reset(context: Context) { waitForNotificationsToSatisfy( forAtLeast = Duration.ZERO, - description = "all Safety Center notifications removed in tear down" + description = "all Safety Center notifications removed in tear down", ) { it.isEmpty() } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt index 0e062692a..c7d195528 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt @@ -175,7 +175,7 @@ object UiTestHelper { fun waitGroupShownOnHomepage(context: Context, group: SafetySourcesGroup) { waitAllTextDisplayed( context.getString(group.titleResId), - context.getString(group.summaryResId) + context.getString(group.summaryResId), ) } |