diff options
138 files changed, 3286 insertions, 1115 deletions
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-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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 e22e3f1e1..402bd5214 100644 --- a/PermissionController/res/values-es/strings.xml +++ b/PermissionController/res/values-es/strings.xml @@ -584,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> 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-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 a718def46..8160770ea 100644 --- a/PermissionController/res/values-eu/strings.xml +++ b/PermissionController/res/values-eu/strings.xml @@ -250,7 +250,7 @@ <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> 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 607745eeb..4ed186f3e 100644 --- a/PermissionController/res/values-fa/strings.xml +++ b/PermissionController/res/values-fa/strings.xml @@ -250,7 +250,7 @@ <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> @@ -346,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> 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 53de5e31c..72f121d1c 100644 --- a/PermissionController/res/values-mr/strings.xml +++ b/PermissionController/res/values-mr/strings.xml @@ -87,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> 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-v35/styles.xml b/PermissionController/res/values-v35/styles.xml index 612125fb1..513754bb8 100644 --- a/PermissionController/res/values-v35/styles.xml +++ b/PermissionController/res/values-v35/styles.xml @@ -197,5 +197,221 @@ <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-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-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-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-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-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/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..4cfed5e79 100644 --- a/PermissionController/res/values/bools.xml +++ b/PermissionController/res/values/bools.xml @@ -20,4 +20,5 @@ <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> </resources> diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml index 9075fa67c..31f3355e4 100644 --- a/PermissionController/res/values/overlayable.xml +++ b/PermissionController/res/values/overlayable.xml @@ -48,6 +48,35 @@ <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="dimen" name="permission_preference_app_icon_size" /> <item type="dimen" name="permission_preference_permission_group_icon_size" /> 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/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/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt index d23225ed3..8b11036e8 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt @@ -73,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 @@ -658,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) == 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/compat/AppPermissionFragmentCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java index 188e3a9d0..de7404ead 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/AppPermissionFragmentCompat.java @@ -19,6 +19,7 @@ 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; @@ -29,6 +30,7 @@ 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; @@ -41,8 +43,10 @@ public class AppPermissionFragmentCompat { * Create an instance of this fragment */ @NonNull - public static PreferenceFragmentCompat createFragment() { - if (SdkLevel.isAtLeastV() && Flags.appPermissionFragmentUsesPreferences()) { + 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(); 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 index 16eaaf6f2..edf6e22e5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageDetailsUseCase.kt @@ -21,6 +21,7 @@ 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 @@ -29,6 +30,7 @@ import com.android.permissioncontroller.permission.domain.model.v31.PermissionTi 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 @@ -72,7 +74,7 @@ class GetPermissionGroupUsageDetailsUseCase( permissionGroup, packageOps.userId, permissionRepository, - packageRepository + packageRepository, ) packageOps } @@ -86,43 +88,61 @@ class GetPermissionGroupUsageDetailsUseCase( } } + // show attribution on T+ for location provider only.. + private fun shouldShowAttributionLabel(packageName: String): Boolean { + return if (com.android.permission.flags.Flags.permissionTimelineAttributionLabelFix()) { + 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 -> - 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 } - } + 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, - ) + attributionLabelOpsMap.map { labelAppOpsEntry -> + DiscretePackageOpsModel( + packageOps.packageName, + packageOps.userId, + appOpAccesses = labelAppOpsEntry.value, + attributionLabel = labelAppOpsEntry.key, + isUserSensitive = packageOps.isUserSensitive, + ) + } + } else { + listOf(packageOps) } } else { listOf(packageOps) } - } else { - listOf(packageOps) } } .flatten() @@ -147,7 +167,7 @@ class GetPermissionGroupUsageDetailsUseCase( packageOps.userId, currentCluster.toMutableList(), packageOps.attributionLabel, - packageOps.isUserSensitive + packageOps.isUserSensitive, ) ) currentCluster.clear() @@ -164,7 +184,7 @@ class GetPermissionGroupUsageDetailsUseCase( packageOps.userId, currentCluster.toMutableList(), packageOps.attributionLabel, - packageOps.isUserSensitive + packageOps.isUserSensitive, ) ) } @@ -220,7 +240,7 @@ class GetPermissionGroupUsageDetailsUseCase( private fun canAccessBeAddedToCluster( currentAccess: DiscreteOpModel, - clusteredAccesses: List<DiscreteOpModel> + clusteredAccesses: List<DiscreteOpModel>, ): Boolean { val clusterOp = clusteredAccesses.last().opName if ( @@ -282,7 +302,7 @@ class GetPermissionGroupUsageDetailsUseCase( listOf( Manifest.permission_group.CAMERA, Manifest.permission_group.LOCATION, - Manifest.permission_group.MICROPHONE + Manifest.permission_group.MICROPHONE, ) permissionGroups.forEach { permissionGroup -> val opNames = 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 2d14260a2..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.toSet() + 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 9f39bd785..36d867b11 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 @@ -46,7 +46,7 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { Manifest.permission_group.CAMERA, 1, Manifest.permission_group.MICROPHONE, - 2 + 2, ) private const val DEFAULT_ORDER: Int = 3 } @@ -54,7 +54,6 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { private val SESSION_ID_KEY = (AutoPermissionUsageFragment::class.java.name + KEY_SESSION_ID) private var showSystem = false - private var finishedInitialLoad = false private var hasSystemApps = false /** Unique Id of a request */ @@ -89,7 +88,7 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { 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 @@ -133,13 +132,13 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { Comparator.comparing { permissionGroupWithUsageCount: Map.Entry<String, Int> -> PERMISSION_GROUP_ORDER.getOrDefault( permissionGroupWithUsageCount.key, - DEFAULT_ORDER + DEFAULT_ORDER, ) } .thenComparing { permissionGroupWithUsageCount: Map.Entry<String, Int> -> mViewModel.getPermissionGroupLabel( requireContext(), - permissionGroupWithUsageCount.key + permissionGroupWithUsageCount.key, ) } ) @@ -153,11 +152,10 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment() { permissionGroupWithUsageCountsEntries[i].value, showSystem, sessionId, - false + false, ) getPreferenceScreen().addPreference(permissionUsagePreference) } - finishedInitialLoad = true setLoading(false) } } 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 8650d99fc..080c7cfdc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionWrapperFragment.java @@ -29,7 +29,7 @@ public class AppPermissionWrapperFragment extends PermissionsCollapsingToolbarBa @NonNull @Override public PreferenceFragmentCompat createPreferenceFragment() { - return AppPermissionFragmentCompat.createFragment(); + return AppPermissionFragmentCompat.createFragment(getContext()); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt index 1cd4ed23a..e7749d827 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionFooterPreference.kt @@ -17,16 +17,21 @@ 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(c: Context) : FooterPreference(c) { +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 (c.resources.getBoolean(R.bool.config_permissionFooterPreferenceIconVisible)) { + 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 index 5e30183ec..010ca28a7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.kt @@ -18,15 +18,30 @@ 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(c: Context) : super(c) + constructor(context: Context) : super(context) - constructor(c: Context, a: AttributeSet) : super(c, a) + 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)) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt index ef1752530..ef95c6c5c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreferenceCategory.kt @@ -18,15 +18,30 @@ 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(c: Context) : super(c) + constructor(context: Context) : super(context) - constructor(c: Context, a: AttributeSet) : super(c, a) + 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)) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java index e8ad9aae6..35650defb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/max35/LegacyAppPermissionFragment.java @@ -105,7 +105,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; - /** * Show and manage a single permission group for an app. * 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 4f8d69e5e..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 @@ -160,7 +160,6 @@ public class PermissionHistoryPreference extends Preference { mPermissionGroup, mPackageName, PERMISSION_DETAILS_INTERACTION__ACTION__TIMELINE_ROW_CLICKED); - mContext.startActivityAsUser(intent, mUserHandle); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext) .setTitle(R.string.privacy_dashboard_emergency_location_dialog_title) .setMessage( 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 index 72e066777..e5dce40b0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v35/SectionPreferenceGroupAdapter.kt @@ -25,6 +25,7 @@ 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 /** @@ -106,7 +107,10 @@ class SectionPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) : } private val Preference.isSectionDivider: Boolean - get() = this is PreferenceCategory || this is FooterPreference + get() = + this is PreferenceCategory || + this is FooterPreference || + this is AppPermissionFooterLinkPreference override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { super.onBindViewHolder(holder, position) 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..f5f511253 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFooterLinkPreference.kt @@ -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 com.android.permissioncontroller.permission.ui.handheld.v36 + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import androidx.annotation.AttrRes +import androidx.annotation.RequiresApi +import androidx.annotation.StyleRes +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 + } +} 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 index 02a811bdd..4fde26c9d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v36/AppPermissionFragment.java @@ -62,20 +62,15 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.RadioButton; -import android.widget.Switch; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; -import androidx.core.widget.NestedScrollView; 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; @@ -84,6 +79,10 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandle 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; @@ -96,13 +95,12 @@ 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.ActionBarShadowController; +import com.android.settingslib.widget.SelectorWithWidgetPreference; import kotlin.Pair; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; /** @@ -120,26 +118,25 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private static final long EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS = 200L; private @NonNull AppPermissionViewModel mViewModel; - private @NonNull ViewGroup mAppPermissionRationaleContainer; - private @NonNull ViewGroup mAppPermissionRationaleContent; - private @NonNull FrameLayout mAllowButtonFrame; - private @NonNull RadioButton mAllowButton; - private @NonNull RadioButton mAllowAlwaysButton; - private @NonNull RadioButton mAllowForegroundButton; - private @NonNull RadioButton mAskOneTimeButton; - private @NonNull RadioButton mAskButton; - private @NonNull RadioButton mSelectButton; - private @NonNull RadioButton mDenyButton; - private @NonNull RadioButton mDenyForegroundButton; - private @NonNull ImageView mEditSelectedPhotosButton; - private @NonNull View mAllowLimitedPhotosLayout; - private @NonNull View mSelectPhotosDivider; - private @NonNull View mLocationAccuracy; - private @NonNull Switch mLocationAccuracySwitch; - private @NonNull View mDivider; - private @NonNull ViewGroup mWidgetFrame; - private @NonNull TextView mPermissionDetails; - private @NonNull NestedScrollView mNestedScrollView; + + 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; @@ -202,20 +199,52 @@ public class AppPermissionFragment extends SettingsWithLargeHeader 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(); - ViewGroup root = (ViewGroup) inflater.inflate(R.layout.app_permission, container, false); mIsInitialLoad = true; setHeader(mPackageIcon, mPackageLabel, null, null, false); - updateHeader(root.requireViewById(R.id.large_header)); String text = null; if (MultiDeviceUtils.isDefaultDeviceId(mPersistentDeviceId)) { @@ -225,87 +254,54 @@ public class AppPermissionFragment extends SettingsWithLargeHeader text = context.getString(R.string.app_permission_header_with_device_name, mPermGroupLabel, deviceName); } - ((TextView) root.requireViewById(R.id.permission_message)).setText(text); + 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); - TextView footer1Link = root.requireViewById(R.id.footer_link_1); - footer1Link.setText(context.getString(R.string.app_permission_footer_app_permissions_link, - mPackageLabel)); - setBottomLinkState(footer1Link, caller, Intent.ACTION_MANAGE_APP_PERMISSIONS); - - TextView footer2Link = root.requireViewById(R.id.footer_link_2); - footer2Link.setText(context.getString(R.string.app_permission_footer_permission_apps_link)); - setBottomLinkState(footer2Link, caller, Intent.ACTION_MANAGE_PERMISSION_APPS); + setBottomLinkState(mFooterLink2, caller, Intent.ACTION_MANAGE_PERMISSION_APPS); Set<String> exemptedPackages = Utils.getExemptedPackages(mRoleManager); - ImageView footerInfoIcon = root.requireViewById(R.id.app_additional_info_icon); - TextView footerInfoText = root.requireViewById(R.id.app_additional_info_text); if (exemptedPackages.contains(mPackageName)) { int additional_info_label = Utils.isStatusBarIndicatorPermission(mPermGroupName) ? R.string.exempt_mic_camera_info_label : R.string.exempt_info_label; - footerInfoText.setText(context.getString(additional_info_label, mPackageLabel)); - footerInfoIcon.setVisibility(View.VISIBLE); - footerInfoText.setVisibility(View.VISIBLE); + mAdditionalInfo.setTitle(context.getString(additional_info_label, mPackageLabel)); + mAdditionalInfo.setVisible(true); } else { - footerInfoIcon.setVisibility(View.GONE); - footerInfoText.setVisibility(View.GONE); + mAdditionalInfo.setVisible(false); } - mAllowButtonFrame = root.requireViewById(R.id.allow_radio_button_frame); - mAllowButton = root.requireViewById(R.id.allow_radio_button); - mAllowAlwaysButton = root.requireViewById(R.id.allow_always_radio_button); - mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button); - mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button); - mAskButton = root.requireViewById(R.id.ask_radio_button); - mSelectButton = root.requireViewById(R.id.select_radio_button); - mDenyButton = root.requireViewById(R.id.deny_radio_button); - mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button); - - mDivider = root.requireViewById(R.id.two_target_divider); - mWidgetFrame = root.requireViewById(R.id.widget_frame); - mPermissionDetails = root.requireViewById(R.id.permission_details); - mLocationAccuracy = root.requireViewById(R.id.location_accuracy); - mLocationAccuracySwitch = root.requireViewById(R.id.location_accuracy_switch); - mAllowLimitedPhotosLayout = root.requireViewById(R.id.radio_select_layout); - mEditSelectedPhotosButton = root.requireViewById(R.id.edit_selected_button); - mSelectPhotosDivider = root.requireViewById(R.id.edit_photos_divider); - mNestedScrollView = root.requireViewById(R.id.nested_scroll_view); - if (mViewModel.getButtonStateLiveData().getValue() != null) { setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); } else { - mAllowButton.setVisibility(View.GONE); - mAllowAlwaysButton.setVisibility(View.GONE); - mAllowForegroundButton.setVisibility(View.GONE); - mAskOneTimeButton.setVisibility(View.GONE); - mAskButton.setVisibility(View.GONE); - mDenyButton.setVisibility(View.GONE); - mDenyForegroundButton.setVisibility(View.GONE); - mLocationAccuracy.setVisibility(View.GONE); - mAllowLimitedPhotosLayout.setVisibility(View.GONE); - mSelectPhotosDivider.setAlpha(0f); - mEditSelectedPhotosButton.setAlpha(0f); + 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(), root); + setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue()); } else { - TextView storageFooter = root.requireViewById(R.id.footer_storage_special_app_access); - storageFooter.setVisibility(View.GONE); + mFooterStorageSpecialAppAccess.setVisible(false); } - mAppPermissionRationaleContainer = - root.requireViewById(R.id.app_permission_rationale_container); - mAppPermissionRationaleContent = - root.requireViewById(R.id.app_permission_rationale_content); - mViewModel.getShowPermissionRationaleLiveData().observe(this, show -> { - showPermissionRationaleDialog(Optional.ofNullable(show).orElse(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 root; + + return super.onCreateView(inflater, container, savedInstanceState); } public void onResume() { @@ -314,28 +310,32 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mPhotoPickerTriggered = false; } + private void showPermissionRationaleDialog(Boolean showPermissionRationale) { + showPermissionRationaleDialog(showPermissionRationale == Boolean.TRUE); + } + private void showPermissionRationaleDialog(boolean showPermissionRationale) { if (!showPermissionRationale) { - mAppPermissionRationaleContainer.setVisibility(View.GONE); + mAppPermissionRationaleContainer.setVisible(false); } else { - mAppPermissionRationaleContainer.setVisibility(View.VISIBLE); - mAppPermissionRationaleContent.setOnClickListener((v) -> { - if (!SdkLevel.isAtLeastU()) { - return; + mAppPermissionRationaleContainer.setVisible(true); + mAppPermissionRationaleContent.setOnPreferenceClickListener((v) -> { + if (SdkLevel.isAtLeastU()) { + mViewModel.showPermissionRationaleActivity(getActivity(), mPermGroupName); } - mViewModel.showPermissionRationaleActivity(getActivity(), mPermGroupName); + return true; }); } } - private void setBottomLinkState(TextView view, String caller, String action) { + 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))) { - view.setVisibility(View.GONE); + preference.setVisible(false); } else { - view.setOnClickListener((v) -> { + preference.setOnPreferenceClickListener((v) -> { Bundle args; if (action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) { args = AppPermissionGroupsFragment.createArgs(mPackageName, mUser, @@ -344,26 +344,11 @@ public class AppPermissionFragment extends SettingsWithLargeHeader args = PermissionAppsFragment.createArgs(mPermGroupName, mSessionId); } mViewModel.showBottomLinkPage(this, action, args); + return true; }); } } - private void setSpecialStorageState(FullStoragePackageState storageState) { - setSpecialStorageState(storageState, getView()); - } - - @Override - public void onStart() { - super.onStart(); - - ActionBar ab = getActivity().getActionBar(); - if (ab != null) { - ab.setElevation(0); - } - - ActionBarShadowController.attachToView(getActivity(), getLifecycle(), mNestedScrollView); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { @@ -384,7 +369,13 @@ public class AppPermissionFragment extends SettingsWithLargeHeader } else if (states == null) { return; } - mAllowButtonFrame.setOnClickListener((v) -> allowButtonFrameClickListener()); + + mAllowButton.setOnClickListener((v) -> { + allowButtonFrameClickListener(); + }); + mAllowButton.setOnDisabledClickListener((v) -> { + allowButtonFrameClickListener(); + }); mAllowAlwaysButton.setOnClickListener((v) -> { markSingleButtonAsChecked(ButtonType.ALLOW_ALWAYS); if (mIsStorageGroup) { @@ -426,13 +417,21 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED, buttonPressed); }); - mEditSelectedPhotosButton.setOnClickListener((v) -> { - ButtonState selectState = states.get(ButtonType.SELECT_PHOTOS); - if (selectState != null && selectState.isChecked() && !mPhotoPickerTriggered) { - mPhotoPickerTriggered = true; - mViewModel.openPhotoPicker(this); - } - }); + + 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 @@ -454,15 +453,17 @@ public class AppPermissionFragment extends SettingsWithLargeHeader APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION; int revokeFineLocation = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION; - mLocationAccuracy.setOnClickListener((v) -> { - mLocationAccuracySwitch.performClick(); - if (mLocationAccuracySwitch.isChecked()) { + + 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)); @@ -473,23 +474,13 @@ public class AppPermissionFragment extends SettingsWithLargeHeader setButtonState(mDenyButton, states.get(ButtonType.DENY)); setButtonState(mDenyForegroundButton, states.get(ButtonType.DENY_FOREGROUND)); setButtonState(mSelectButton, states.get(ButtonType.SELECT_PHOTOS)); - if (mSelectButton.getVisibility() == View.VISIBLE) { - mAllowButton.setText(R.string.app_permission_button_always_allow_all); + if (mSelectButton.isVisible()) { + mAllowButton.setTitle(R.string.app_permission_button_always_allow_all); } else { - mAllowButton.setText(R.string.app_permission_button_allow); + mAllowButton.setTitle(R.string.app_permission_button_allow); } - ButtonState locationAccuracyState = states.get(ButtonType.LOCATION_ACCURACY); - if (!locationAccuracyState.isShown()) { - mLocationAccuracy.setVisibility(View.GONE); - } else { - mLocationAccuracy.setVisibility(View.VISIBLE); - } - mLocationAccuracySwitch.setChecked(locationAccuracyState.isChecked()); - if (!locationAccuracyState.isEnabled()) { - mLocationAccuracy.setEnabled(false); - mLocationAccuracySwitch.setEnabled(false); - } + setButtonState(mLocationAccuracySwitch, states.get(ButtonType.LOCATION_ACCURACY)); mIsInitialLoad = false; @@ -509,60 +500,38 @@ public class AppPermissionFragment extends SettingsWithLargeHeader } } - private void setButtonState(CompoundButton button, AppPermissionViewModel.ButtonState state) { - int visible = state.isShown() ? View.VISIBLE : View.GONE; - button.setVisibility(visible); - if (state.isShown()) { - button.setChecked(state.isChecked()); - button.setEnabled(state.isEnabled()); - } - if (mIsInitialLoad) { - button.jumpDrawablesToCurrentState(); - } - - 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); - mEditSelectedPhotosButton.setAlpha(endOpacity); - return; - } - mEditSelectedPhotosButton.animate().alpha(endOpacity) - .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); - mSelectPhotosDivider.animate().alpha(endOpacity) - .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); - } + private void setButtonState(TwoStatePreference button, + AppPermissionViewModel.ButtonState state) { + button.setVisible(state.isShown()); + button.setChecked(state.isChecked()); + button.setEnabled(state.isEnabled()); } - private void setSpecialStorageState(FullStoragePackageState storageState, View v) { - if (v == null) { - return; - } + private boolean isButtonChecked(Map<ButtonType, ButtonState> state, ButtonType button) { + return state.containsKey(button) && state.get(button).isChecked(); + } - TextView textView = v.requireViewById(R.id.footer_storage_special_app_access); - if (mAllowButton == null || !mIsStorageGroup) { - textView.setVisibility(View.GONE); + private void setSpecialStorageState(FullStoragePackageState storageState) { + if (!mAllowButton.isVisible() || !mIsStorageGroup) { + mFooterStorageSpecialAppAccess.setVisible(false); return; } - mAllowAlwaysButton.setText(R.string.app_permission_button_allow_all_files); - mAllowForegroundButton.setText(R.string.app_permission_button_allow_media_only); + mAllowAlwaysButton.setTitle(R.string.app_permission_button_allow_all_files); + mAllowForegroundButton.setTitle(R.string.app_permission_button_allow_media_only); if (storageState == null) { - textView.setVisibility(View.GONE); + mFooterStorageSpecialAppAccess.setVisible(false); return; } if (storageState.isLegacy()) { - mAllowButton.setText(R.string.app_permission_button_allow_all_files); - textView.setVisibility(View.GONE); + mAllowButton.setTitle(R.string.app_permission_button_allow_all_files); + mFooterStorageSpecialAppAccess.setVisible(false); return; } - textView.setText(R.string.app_permission_footer_special_file_access); - textView.setVisibility(View.VISIBLE); + mFooterStorageSpecialAppAccess.setVisible(true); } private void setResult(@GrantPermissionsViewHandler.Result int result) { @@ -578,53 +547,37 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private void setDetail(Pair<Integer, Integer> detailResIds) { if (detailResIds == null) { - mWidgetFrame.setVisibility(View.GONE); - mDivider.setVisibility(View.GONE); + mDetails.setOnSecondTargetClickListener(null); return; } - mWidgetFrame.setVisibility(View.VISIBLE); if (detailResIds.getSecond() != null) { // If the permissions are individually controlled, also show a link to the page that // lets you control them. - mDivider.setVisibility(View.VISIBLE); - showRightIcon(R.drawable.ic_settings); + mDetails.setExtraWidgetIconRes(R.drawable.ic_settings); Bundle args = AllAppPermissionsFragment.createArgs(mPackageName, mPermGroupName, mUser); - mWidgetFrame.setOnClickListener(v -> mViewModel.showAllPermissions(this, args)); - mPermissionDetails.setText(getPreferenceManager().getContext().getString( + mDetails.setOnSecondTargetClickListener((v) -> + mViewModel.showAllPermissions(this, args)); + mDetails.setSummary(getPreferenceManager().getContext().getString( detailResIds.getFirst(), detailResIds.getSecond())); } else { - mPermissionDetails.setText(getPreferenceManager().getContext().getString( + mDetails.setOnSecondTargetClickListener(null); + mDetails.setSummary(getPreferenceManager().getContext().getString( detailResIds.getFirst())); } - mPermissionDetails.setVisibility(View.VISIBLE); - + mDetails.setVisible(true); } private void setAdminSupportDetail(EnforcedAdmin admin) { if (admin != null) { - showRightIcon(R.drawable.ic_info); - mWidgetFrame.setOnClickListener(v -> - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin) - ); + mDetails.setExtraWidgetIconRes(R.drawable.ic_info_outline); + mDetails.setOnSecondTargetClickListener((v) -> + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin)); } else { - mWidgetFrame.removeAllViews(); + mDetails.setOnSecondTargetClickListener(null); } } /** - * Show the given icon on the right of the first radio button. - * - * @param iconId the resourceId of the drawable to use. - */ - private void showRightIcon(int iconId) { - mWidgetFrame.removeAllViews(); - ImageView imageView = new ImageView(getPreferenceManager().getContext()); - imageView.setImageResource(iconId); - mWidgetFrame.addView(imageView); - mWidgetFrame.setVisibility(View.VISIBLE); - } - - /** * 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. * 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/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt deleted file mode 100644 index 1369bfdaa..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.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 (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/wear/elements/AnnotatedText.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt index bcdf3b661..34c7cee9a 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) +): 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(text.toString()) } - } else { - AnnotatedString(text.toString()) - } + return annotatedString +} private fun AnnotatedString.Builder.addClickableSpan( span: ClickableSpan, 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 53013def7..d8f340a7b 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 @@ -282,6 +282,7 @@ internal fun Scaffold( color = MaterialTheme.colors.onSurfaceVariant ), modifier = modifier, + shouldCapitalize = true ) } } 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/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 99aa4baa9..0d89e0501 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,19 +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 @@ -37,12 +45,16 @@ 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_SYSTEM_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 kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Before import org.junit.Test @@ -71,11 +83,14 @@ class HibernationPolicyTest { 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 @@ -83,6 +98,7 @@ class HibernationPolicyTest { private lateinit var sharedPreferences: SharedPreferences private lateinit var mockitoSession: MockitoSession private lateinit var filesDir: File + private lateinit var userHandle: UserHandle @Before fun setup() { @@ -100,11 +116,15 @@ class HibernationPolicyTest { `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) @@ -207,6 +227,25 @@ class HibernationPolicyTest { .isNotEqualTo(systemTimeSnapshot) } + @Test + 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( @@ -223,4 +262,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 index da2d05f63..c2040f76b 100644 --- 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 @@ -34,6 +34,7 @@ import com.android.permissioncontroller.permission.domain.model.v31.PermissionTi 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 @@ -56,9 +57,11 @@ 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 @@ -88,11 +91,13 @@ class GetPermissionGroupUsageDetailsUseCaseTest { 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( @@ -102,7 +107,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { systemPackageName to getPackageInfoModel( systemPackageName, - applicationFlags = ApplicationInfo.FLAG_SYSTEM + applicationFlags = ApplicationInfo.FLAG_SYSTEM, ), ) .toMutableMap() @@ -124,7 +129,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { emit( listOf( DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), - DiscretePackageOpsModel(guestUserPkgName, guestUser.identifier, appOpEvents) + DiscretePackageOpsModel(guestUserPkgName, guestUser.identifier, appOpEvents), ) ) } @@ -150,11 +155,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { emit( listOf( DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), - DiscretePackageOpsModel( - testPackageName, - privateProfile.identifier, - appOpEvents - ), + DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents), ) ) } @@ -168,8 +169,8 @@ class GetPermissionGroupUsageDetailsUseCaseTest { currentUserProfiles = listOf(currentUser.identifier, privateProfile.identifier), quietUserProfiles = listOf(privateProfile.identifier), - showInQuiteModeProfiles = listOf(privateProfile.identifier) - ) + showInQuiteModeProfiles = listOf(privateProfile.identifier), + ), ) val permissionTimelineUsages = getResult(underTest, this) @@ -190,11 +191,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { emit( listOf( DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), - DiscretePackageOpsModel( - testPackageName, - privateProfile.identifier, - appOpEvents - ), + DiscretePackageOpsModel(testPackageName, privateProfile.identifier, appOpEvents), ) ) } @@ -208,7 +205,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { currentUserProfiles = listOf(currentUser.identifier, privateProfile.identifier), quietUserProfiles = listOf(privateProfile.identifier), - ) + ), ) val permissionTimelineUsages = getResult(underTest, this) @@ -254,7 +251,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -280,7 +277,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -303,23 +300,23 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), - MINUTES.toMillis(1) + MINUTES.toMillis(1), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(2), - MINUTES.toMillis(1) + MINUTES.toMillis(1), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(3), - MINUTES.toMillis(2) + MINUTES.toMillis(2), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -344,23 +341,19 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_FINE_LOCATION, hours3 + MINUTES.toMillis(59), - -1 + -1, ), DiscreteOpModel( AppOpsManager.OPSTR_FINE_LOCATION, hours4 + MINUTES.toMillis(0), - -1 - ), - DiscreteOpModel( - AppOpsManager.OPSTR_FINE_LOCATION, - hours4 + MINUTES.toMillis(1), - -1 + -1, ), + DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, hours4 + MINUTES.toMillis(1), -1), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -383,18 +376,18 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), - MINUTES.toMillis(3) + MINUTES.toMillis(3), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(3), - MINUTES.toMillis(2) + MINUTES.toMillis(2), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -420,7 +413,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -448,18 +441,18 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), - MINUTES.toMillis(3) + MINUTES.toMillis(3), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(5), - MINUTES.toMillis(5) + MINUTES.toMillis(5), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -483,13 +476,11 @@ class GetPermissionGroupUsageDetailsUseCaseTest { @Test fun singleDiscreteAccess() = runTest { val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1), - ) + listOf(DiscreteOpModel(AppOpsManager.OPSTR_FINE_LOCATION, MINUTES.toMillis(1), -1)) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -512,13 +503,13 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), - MINUTES.toMillis(3) - ), + MINUTES.toMillis(3), + ) ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -541,29 +532,29 @@ class GetPermissionGroupUsageDetailsUseCaseTest { DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), - MINUTES.toMillis(2) + 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) + MINUTES.toMillis(15), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(4), - MINUTES.toMillis(1) + MINUTES.toMillis(1), ), DiscreteOpModel( AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(6), - MINUTES.toMillis(1) + MINUTES.toMillis(1), ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -582,9 +573,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { @Test fun verifyUserSensitiveFlags() = runTest { val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1), - ) + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1)) val discretePackageOps = flow { emit( listOf( @@ -620,13 +609,11 @@ class GetPermissionGroupUsageDetailsUseCaseTest { @Test fun verifyNotUserSensitiveFlagsForSystemPackage() = runTest { val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1), - ) + listOf(DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1)) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(systemPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -651,14 +638,12 @@ class GetPermissionGroupUsageDetailsUseCaseTest { @Test fun verifyCameraUserSensitiveFlagsForTelecomPackage() = runTest { val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_CAMERA, MINUTES.toMillis(1), -1), - ) + 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), + DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents) ) ) } @@ -683,14 +668,12 @@ class GetPermissionGroupUsageDetailsUseCaseTest { @Test fun verifyLocationUserSensitiveFlagsForTelecomPackage() = runTest { val appOpEvents = - listOf( - DiscreteOpModel(AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1), - ) + 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), + DiscretePackageOpsModel(TELECOM_PACKAGE, currentUser.identifier, appOpEvents) ) ) } @@ -714,7 +697,74 @@ class GetPermissionGroupUsageDetailsUseCaseTest { } @Test - fun verifyAttributionTagsAreGroupedAndClustered() = runTest { + @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), + ) + 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. @@ -722,38 +772,38 @@ class GetPermissionGroupUsageDetailsUseCaseTest { AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(1), -1, - "tag1" + "tag1", ), DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(2), -1, - "tag1" + "tag1", ), DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(3), -1, - "tag3" + "tag3", ), // The access at minute 4 should not be grouped or clustered. DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(4), -1, - "tag2" + "tag2", ), DiscreteOpModel( AppOpsManager.OPSTR_COARSE_LOCATION, MINUTES.toMillis(8), -1, - "tag2" + "tag2", ), ) val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -766,14 +816,14 @@ class GetPermissionGroupUsageDetailsUseCaseTest { testPackageName, true, attributionTagToLabelRes, - attributionsMap + attributionsMap, ) val underTest = getPermissionGroupUsageDetailsUseCase( LOCATION_PERMISSION_GROUP, discretePackageOps, - packageRepository = FakePackageRepository(packageInfos, packageAttributions) + packageRepository = FakePackageRepository(packageInfos, packageAttributions), ) val permissionTimelineUsages = getResult(underTest, this) @@ -805,7 +855,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -829,7 +879,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { val discretePackageOps = flow { emit( listOf( - DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents), + DiscretePackageOpsModel(testPackageName, currentUser.identifier, appOpEvents) ) ) } @@ -843,7 +893,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { private fun TestScope.getResult( useCase: GetPermissionGroupUsageDetailsUseCase, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, ): List<PermissionTimelineUsageModel> { val usages by collectLastValue(useCase(coroutineScope)) return (usages as PermissionTimelineUsageModelWrapper.Success).timelineUsageModels @@ -854,7 +904,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { discreteUsageFlow: Flow<List<DiscretePackageOpsModel>>, permissionFlags: Map<String, Int> = emptyMap(), userRepository: UserRepository = FakeUserRepository(listOf(currentUser.identifier)), - packageRepository: PackageRepository = FakePackageRepository(packageInfos) + packageRepository: PackageRepository = FakePackageRepository(packageInfos), ): GetPermissionGroupUsageDetailsUseCase { val permissionRepository = FakePermissionRepository(permissionFlags) val appOpUsageRepository = FakeAppOpRepository(emptyFlow(), discreteUsageFlow) @@ -865,7 +915,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { permissionRepository, appOpUsageRepository, roleRepository, - userRepository + userRepository, ) } @@ -877,7 +927,7 @@ class GetPermissionGroupUsageDetailsUseCaseTest { listOf( PackageInfo.REQUESTED_PERMISSION_GRANTED, PackageInfo.REQUESTED_PERMISSION_GRANTED, - PackageInfo.REQUESTED_PERMISSION_GRANTED + PackageInfo.REQUESTED_PERMISSION_GRANTED, ), applicationFlags: Int = 0, ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags) diff --git a/SafetyCenter/Resources/res/values-ar/strings.xml b/SafetyCenter/Resources/res/values-ar/strings.xml index 14ec20a51..0fc5079db 100644 --- a/SafetyCenter/Resources/res/values-ar/strings.xml +++ b/SafetyCenter/Resources/res/values-ar/strings.xml @@ -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/flags/Android.bp b/flags/Android.bp index aba1e44a9..d22da26c3 100644 --- a/flags/Android.bp +++ b/flags/Android.bp @@ -36,6 +36,7 @@ java_aconfig_library { libs: ["framework-configinfrastructure.stubs.module_lib"], visibility: [ "//packages/modules/Permission:__subpackages__", + "//vendor:__subpackages__", ], apex_available: [ "com.android.permission", diff --git a/flags/flags.aconfig b/flags/flags.aconfig index c94614654..5f3d1cc93 100644 --- a/flags/flags.aconfig +++ b/flags/flags.aconfig @@ -108,3 +108,12 @@ flag { bug: "349675008" 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 +} diff --git a/tests/apex/Android.bp b/tests/apex/Android.bp index 83bf4e252..18f1bea75 100644 --- a/tests/apex/Android.bp +++ b/tests/apex/Android.bp @@ -27,6 +27,7 @@ android_test { ], static_libs: [ "service-permission.impl", + "services.core", "androidx.test.rules", "androidx.test.ext.junit", "androidx.test.ext.truth", 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/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt index 7ecc51ded..4d166939a 100644 --- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt +++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt @@ -320,8 +320,7 @@ class AppPermissionsTest { } private fun verifyPermissionMessage() { - val actualText = UiAutomatorUtils2.waitFindObject(By.res(PERMISSION_MESSAGE_ID)).text - assertEquals(permissionMessage, actualText) + UiAutomatorUtils2.waitFindObject(By.text(permissionMessage)) } private fun getGrantInfoMap(): Map<String, List<String>> { @@ -441,8 +440,6 @@ class AppPermissionsTest { "com.android.permissioncontroller:id/deny_radio_button" private const val TITLE = "android:id/title" private const val RECYCLER_VIEW = "com.android.permissioncontroller:id/recycler_view" - private const val PERMISSION_MESSAGE_ID = - "com.android.permissioncontroller:id/permission_message" private const val NEW_WINDOW_TIMEOUT_MILLIS: Long = 20_000 } } 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 cbb8abc0a..2332ff0c7 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 = @@ -994,7 +991,9 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } protected fun clickPermissionRationaleContentInAppPermission() { - clickAndWaitForWindowTransition(By.res(APP_PERMISSION_RATIONALE_CONTENT_VIEW)) + clickAndWaitForWindowTransition( + By.text(getPermissionControllerString(APP_PERMISSION_RATIONALE_SUBTITLE_TEXT)) + ) } protected fun clickPermissionRationaleViewInGrantDialog() { diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt index ae08ffa10..14901eb2a 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt @@ -44,6 +44,8 @@ class PermissionReviewTapjackingTest : BaseUsePermissionTest() { installPackage(APP_APK_PATH_22) installPackage(HELPER_APP_OVERLAY) + Thread.sleep(1000) + SystemUtil.runShellCommandOrThrow( "appops set $HELPER_PACKAGE_NAME android:system_alert_window allow" ) diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp index e392109db..f0095b7dd 100644 --- a/tests/cts/role/Android.bp +++ b/tests/cts/role/Android.bp @@ -48,6 +48,6 @@ android_test { ":CtsRoleTestApp", ":CtsRoleTestApp28", ":CtsRoleTestApp33WithoutInCallService", - ":CtsRoleTestAppForProfile", + ":CtsRoleTestAppClone", ], } diff --git a/tests/cts/role/AndroidTest.xml b/tests/cts/role/AndroidTest.xml index a53fa62d0..bfdcf2829 100644 --- a/tests/cts/role/AndroidTest.xml +++ b/tests/cts/role/AndroidTest.xml @@ -42,7 +42,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 b64de5665..4ba8b33e7 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java +++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java @@ -66,7 +66,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; @@ -108,10 +108,10 @@ public class RoleManagerTest { 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 +183,20 @@ public class RoleManagerTest { private String mRoleHolder; @Before - public void saveRoleHolder() throws Exception { + public void setUp() throws Exception { + 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 +206,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 +216,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)); } @@ -855,15 +867,15 @@ public class RoleManagerTest { UserHandle privateProfile = sDeviceState.privateProfile().userHandle(); assertThat(privateProfile).isNotNull(); installPackage(APP_APK_PATH, privateProfile); - installPackage(APP_FOR_PROFILE_APK_PATH, privateProfile); - addRoleHolderAsUser(ROLE_NAME, APP_FOR_PROFILE_PACKAGE_NAME, privateProfile); + installPackage(APP_CLONE_APK_PATH, 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)) @@ -877,7 +889,7 @@ public class RoleManagerTest { pressBack(); uninstallPackage(APP_PACKAGE_NAME, privateProfile); - uninstallPackage(APP_FOR_PROFILE_APK_PATH, privateProfile); + uninstallPackage(APP_CLONE_PACKAGE_NAME, privateProfile); } @Test 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..83d4f78ad 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt +++ b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt @@ -44,7 +44,20 @@ class RoleShellCommandTest { private var wasBypassingRoleQualification: Boolean = false @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 +65,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 @@ -196,5 +208,7 @@ class RoleShellCommandTest { private const val ROLE_NAME = RoleManager.ROLE_BROWSER 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/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt index ff5dcc1f6..7c2062e8d 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 @@ -43,11 +43,11 @@ 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.multiuser.annotations.EnsureHasAdditionalUser +import com.android.bedstead.multiuser.annotations.EnsureHasCloneProfile import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile -import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile -import com.android.bedstead.harrier.annotations.EnsureHasNoPrivateProfile +import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile +import com.android.bedstead.multiuser.annotations.EnsureHasNoPrivateProfile import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile import com.android.bedstead.enterprise.annotations.EnsureHasDeviceOwner import com.android.bedstead.enterprise.annotations.EnsureHasNoDeviceOwner |