summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/res/values-af/strings.xml1
-rw-r--r--PermissionController/res/values-am/strings.xml1
-rw-r--r--PermissionController/res/values-ar/strings.xml1
-rw-r--r--PermissionController/res/values-as/strings.xml1
-rw-r--r--PermissionController/res/values-az/strings.xml1
-rw-r--r--PermissionController/res/values-b+sr+Latn/strings.xml1
-rw-r--r--PermissionController/res/values-be/strings.xml1
-rw-r--r--PermissionController/res/values-bg/strings.xml1
-rw-r--r--PermissionController/res/values-bn/strings.xml1
-rw-r--r--PermissionController/res/values-bs/strings.xml1
-rw-r--r--PermissionController/res/values-ca/strings.xml1
-rw-r--r--PermissionController/res/values-cs/strings.xml1
-rw-r--r--PermissionController/res/values-da/strings.xml1
-rw-r--r--PermissionController/res/values-de/strings.xml1
-rw-r--r--PermissionController/res/values-el/strings.xml1
-rw-r--r--PermissionController/res/values-en-rAU/strings.xml1
-rw-r--r--PermissionController/res/values-en-rCA/strings.xml1
-rw-r--r--PermissionController/res/values-en-rGB/strings.xml1
-rw-r--r--PermissionController/res/values-en-rIN/strings.xml1
-rw-r--r--PermissionController/res/values-en-rXC/strings.xml1
-rw-r--r--PermissionController/res/values-es-rUS/strings.xml1
-rw-r--r--PermissionController/res/values-es/strings.xml1
-rw-r--r--PermissionController/res/values-et/strings.xml1
-rw-r--r--PermissionController/res/values-eu/strings.xml1
-rw-r--r--PermissionController/res/values-fa/strings.xml1
-rw-r--r--PermissionController/res/values-fi/strings.xml1
-rw-r--r--PermissionController/res/values-fr-rCA/strings.xml1
-rw-r--r--PermissionController/res/values-fr/strings.xml1
-rw-r--r--PermissionController/res/values-gl/strings.xml1
-rw-r--r--PermissionController/res/values-gu/strings.xml1
-rw-r--r--PermissionController/res/values-hi/strings.xml1
-rw-r--r--PermissionController/res/values-hr/strings.xml1
-rw-r--r--PermissionController/res/values-hu/strings.xml1
-rw-r--r--PermissionController/res/values-hy/strings.xml1
-rw-r--r--PermissionController/res/values-in/strings.xml1
-rw-r--r--PermissionController/res/values-is/strings.xml1
-rw-r--r--PermissionController/res/values-it/strings.xml1
-rw-r--r--PermissionController/res/values-iw/strings.xml1
-rw-r--r--PermissionController/res/values-ja/strings.xml1
-rw-r--r--PermissionController/res/values-ka/strings.xml1
-rw-r--r--PermissionController/res/values-kk/strings.xml1
-rw-r--r--PermissionController/res/values-km/strings.xml1
-rw-r--r--PermissionController/res/values-kn/strings.xml1
-rw-r--r--PermissionController/res/values-ko/strings.xml1
-rw-r--r--PermissionController/res/values-ky/strings.xml1
-rw-r--r--PermissionController/res/values-lo/strings.xml1
-rw-r--r--PermissionController/res/values-lt/strings.xml1
-rw-r--r--PermissionController/res/values-lv/strings.xml1
-rw-r--r--PermissionController/res/values-mk/strings.xml1
-rw-r--r--PermissionController/res/values-ml/strings.xml1
-rw-r--r--PermissionController/res/values-mn/strings.xml1
-rw-r--r--PermissionController/res/values-mr/strings.xml1
-rw-r--r--PermissionController/res/values-ms/strings.xml1
-rw-r--r--PermissionController/res/values-my/strings.xml1
-rw-r--r--PermissionController/res/values-nb/strings.xml1
-rw-r--r--PermissionController/res/values-ne/strings.xml1
-rw-r--r--PermissionController/res/values-nl/strings.xml1
-rw-r--r--PermissionController/res/values-or/strings.xml1
-rw-r--r--PermissionController/res/values-pa/strings.xml1
-rw-r--r--PermissionController/res/values-pl/strings.xml1
-rw-r--r--PermissionController/res/values-pt-rBR/strings.xml1
-rw-r--r--PermissionController/res/values-pt-rPT/strings.xml1
-rw-r--r--PermissionController/res/values-pt/strings.xml1
-rw-r--r--PermissionController/res/values-ro/strings.xml1
-rw-r--r--PermissionController/res/values-ru/strings.xml1
-rw-r--r--PermissionController/res/values-si/strings.xml1
-rw-r--r--PermissionController/res/values-sk/strings.xml1
-rw-r--r--PermissionController/res/values-sl/strings.xml1
-rw-r--r--PermissionController/res/values-sq/strings.xml1
-rw-r--r--PermissionController/res/values-sr/strings.xml1
-rw-r--r--PermissionController/res/values-sv/strings.xml1
-rw-r--r--PermissionController/res/values-sw/strings.xml1
-rw-r--r--PermissionController/res/values-ta/strings.xml1
-rw-r--r--PermissionController/res/values-te/strings.xml1
-rw-r--r--PermissionController/res/values-th/strings.xml1
-rw-r--r--PermissionController/res/values-tl/strings.xml1
-rw-r--r--PermissionController/res/values-tr/strings.xml1
-rw-r--r--PermissionController/res/values-uk/strings.xml1
-rw-r--r--PermissionController/res/values-ur/strings.xml1
-rw-r--r--PermissionController/res/values-uz/strings.xml1
-rw-r--r--PermissionController/res/values-vi/strings.xml1
-rw-r--r--PermissionController/res/values-zh-rCN/strings.xml1
-rw-r--r--PermissionController/res/values-zh-rHK/strings.xml1
-rw-r--r--PermissionController/res/values-zh-rTW/strings.xml1
-rw-r--r--PermissionController/res/values-zu/strings.xml1
-rw-r--r--PermissionController/res/values/strings.xml3
-rw-r--r--PermissionController/res/values/themes.xml8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/incident/PendingList.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java)52
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/AutoGrantPermissionsNotifier.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt31
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java772
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java338
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java127
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java151
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java164
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java422
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java144
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java102
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt132
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt)4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt120
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java)4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java (renamed from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java)15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java2
121 files changed, 1367 insertions, 1449 deletions
diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml
index 14c155127..dbbe4516c 100644
--- a/PermissionController/res/values-af/strings.xml
+++ b/PermissionController/res/values-af/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Programtoestemmings"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ongebruikte programme"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Geen ongebruikte programme nie"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ongebruikte programme"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktiveer program"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"As jy hierdie program deaktiveer, sal Android en ander programme dalk nie meer soos bedoel werk nie. Hou in gedagte dat jy nie hierdie program kan uitvee nie, want dit is vooraf op jou toestel geïnstalleer. Deur dit te deaktiveer, skakel jy hierdie program af en versteek jy dit op jou toestel."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Toestemmingbestuurder"</string>
diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml
index ad5584b87..2da03c381 100644
--- a/PermissionController/res/values-am/strings.xml
+++ b/PermissionController/res/values-am/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"የመተግበሪያ ፈቃዶች"</string>
<string name="unused_apps" msgid="2058057455175955094">"ጥቅም ላይ ያልዋሉ መተግበሪያዎች"</string>
<string name="no_unused_apps" msgid="12809387670415295">"አገልግሎት ላይ ያልዋሉ መተግበሪያዎች የሉም"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ሥራ ላይ ያልዋሉ መተግበሪያዎች"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"መተግበሪያን አሰናክል"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ይህን መተግበሪያ ካሰናከሉት ከዚህ በኋላ Android እና ሌሎች መተግበሪያዎች እንደተፈለገው ላይሠሩ ይችላሉ። ያስታውሱ፣ በእርስዎ መሣሪያ ላይ አስቀድሞ ተጭኖ ስለሚመጣ ይህን መተግበሪያ መሰረዝ አይችሉም። በማሰናከልዎት፣ ይህን መተግበሪያ ያጠፉታል እና በእርስዎ መሣሪያ ላይ ይደብቁታል።"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"የፈቃድ አቀናባሪ"</string>
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml
index 820a865bf..7ab1c2e13 100644
--- a/PermissionController/res/values-ar/strings.xml
+++ b/PermissionController/res/values-ar/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"أذونات التطبيق"</string>
<string name="unused_apps" msgid="2058057455175955094">"التطبيقات غير المستخدمة"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ما مِن تطبيقات غير مستخدمة."</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"التطبيقات غير المستخدمة: 0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"إيقاف التطبيق"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"‏قد يؤدي إيقاف هذا التطبيق إلى عدم عمل نظام Android وتطبيقات أخرى على النحو المنشود. تجدر الإشارة إلى أنه لا يمكنك حذف هذا التطبيق لأنه من التطبيقات المثبّتة تلقائيًا على جهازك. ويعني إيقاف التطبيق أنه سيتم إيقافه وإخفاؤه على جهازك."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"مدير الأذونات"</string>
diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml
index 769f6c6cf..4bd5d7e81 100644
--- a/PermissionController/res/values-as/strings.xml
+++ b/PermissionController/res/values-as/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"এপৰ অনুমতি"</string>
<string name="unused_apps" msgid="2058057455175955094">"অব্যৱহৃত এপ্‌সমূহ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ব্যৱহাৰ নকৰা কোনো এপ্‌ নাই"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"০ টা অব্যৱহৃত এপ্‌"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"এপ্ অক্ষম কৰক"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"আপুনি যদি এই এপ্‌টো অক্ষম কৰে, Android আৰু অন্য এপ্‌সমূহে বিচৰাৰ দৰে আৰু কাম নকৰিব পাৰে। মনত ৰাখিব, এই এপ্‌টো আপোনাৰ ডিভাইচটোত আগৰেপৰা ইনষ্টল কৰি থোৱা আছে বাবে আপুনি ইয়াক মচিব নোৱাৰে। অক্ষম কৰি আপুনি এই এপ্‌টো অফ কৰে আৰু ইয়াক আপোনাৰ ডিভাইচৰ পৰা লুকুৱায়।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"অনুমতি পৰিচালক"</string>
diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml
index 1558be330..22072361d 100644
--- a/PermissionController/res/values-az/strings.xml
+++ b/PermissionController/res/values-az/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Tətbiq icazələri"</string>
<string name="unused_apps" msgid="2058057455175955094">"İşlədilməyən tətbiqlər"</string>
<string name="no_unused_apps" msgid="12809387670415295">"İstifadə olunmayan tətbiq yoxdur"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 istifadə olunmayan tətbiq"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Tətbiqi deaktiv edin"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Əgər bu tətbiqi deaktiv etsəniz, Android və digər tətbiqlər düzgün işləməyə bilər. Yadda saxlayın ki, cihazda əvvəlcədən quraşdırıldığına görə bu tətbiqi silə bilməzsiniz. Deaktiv etməklə, bu tətbiqi söndürür və cihazda gizlədirsiniz."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"İcazə meneceri"</string>
diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 0ec619e8c..c9af8c888 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Dozvole za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikacije koje se ne koriste"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema aplik. koje se ne koriste"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikac. koje se ne koriste"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Onemogući aplikaciju"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ako onemogućite ovu aplikaciju, Android i druge aplikacije možda više neće raditi ispravno. Imajte na umu da ne možete da izbrišete ovu aplikaciju jer je bila unapred instalirana na uređaju. Ako je onemogućite, isključićete je i sakriti na uređaju."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Menadžer dozvola"</string>
diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml
index 5d7cb2bba..57a1524d0 100644
--- a/PermissionController/res/values-be/strings.xml
+++ b/PermissionController/res/values-be/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Дазволы праграмы"</string>
<string name="unused_apps" msgid="2058057455175955094">"Праграмы, якія не выкарыстоўваюцца"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Няма нескарыстаных праграм"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 праграм не ў карыстанні"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Адключыць праграму"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Калі вы адключыце гэту праграму, іншыя праграмы могуць працаваць неналежным чынам. Майце на ўвазе, што вы не можаце выдаліць гэту праграму, таму што яна папярэдне ўсталявана на вашай прыладзе. Аднак вы можаце выключыць гэту праграму і схаваць яе."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Менеджар дазволаў"</string>
diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml
index 1c42e6993..e18e378d1 100644
--- a/PermissionController/res/values-bg/strings.xml
+++ b/PermissionController/res/values-bg/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Разрешения за приложенията"</string>
<string name="unused_apps" msgid="2058057455175955094">"Неизползвани приложения"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Няма неизползвани приложения"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 неизползвани приложения"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Деактивиране на приложението"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ако деактивирате това приложение, Android и други приложения може да спрат да работят както трябва. Имайте предвид, че не можете да изтриете това приложение, тъй като е предварително инсталирано на устройството ви. Като деактивирате приложението, го изключвате и скривате на устройството си."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Мениджър на разрешенията"</string>
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml
index 051d88bb4..daec586c5 100644
--- a/PermissionController/res/values-bn/strings.xml
+++ b/PermissionController/res/values-bn/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"অ্যাপের অনুমতি"</string>
<string name="unused_apps" msgid="2058057455175955094">"অব্যবহৃত অ্যাপ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"অব্যবহৃত কোনও অ্যাপ নেই"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"ব্যবহার না করা একটিও অ্যাপ নেই"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"অ্যাপ বন্ধ করুন"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"আপনি এই অ্যাপ বন্ধ করে দিলে, Android এবং অন্যান্য অ্যাপ যেভাবে কাজ করার কথা সেইভাবে আর নাও কাজ করতে পারে। মনে রাখবেন যে এই অ্যাপ আপনার ডিভাইসে আগে থেকেই ইনস্টল করা আছে বলে এটি আপনি মুছে ফেলতে পারবেন না। বন্ধ করার বিকল্পে ট্যাপ করে, আপনি অ্যাপটিকে বন্ধ করে ও লুকিয়েও রাখতে পারবেন।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"অনুমতি ম্যানেজার"</string>
diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml
index cc11f217c..3ed31683d 100644
--- a/PermissionController/res/values-bs/strings.xml
+++ b/PermissionController/res/values-bs/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Odobrenja za aplikaciju"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nekorištene aplikacije"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema nekorištenih aplikacija"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 nekorištenih aplikacija"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Onemogući aplikaciju"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ako onemogućite ovu aplikaciju, moguće je da Android i druge aplikacije više neće funkcionirati ispravno. Imajte na umu da ovu aplikaciju ne možete izbrisati jer je unaprijed instalirana na vašem uređaju. Ako onemogućite aplikaciju, isključit ćete je i sakriti na uređaju."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Upravitelj odobrenja"</string>
diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml
index f52a84160..7a6065481 100644
--- a/PermissionController/res/values-ca/strings.xml
+++ b/PermissionController/res/values-ca/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permisos d\'aplicacions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicacions no utilitzades"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Cap aplicació sense utilitzar"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicacions no utilitzades"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desactiva l\'aplicació"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si desactives aquesta aplicació, és possible que Android i altres aplicacions deixin de funcionar com esperes. Tingues en compte que no pots suprimir aquesta aplicació perquè estava preinstal·lada al dispositiu. Si la desactives, l\'aplicació s\'amagarà al dispositiu."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestor de permisos"</string>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 48d2f43cf..65585d9fe 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Oprávnění aplikací"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nepoužívané aplikace"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Žádné nepoužívané aplikace"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Žádné nepoužívané aplikace"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktivovat aplikaci"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Pokud tuto aplikaci deaktivujete, systém Android a ostatní aplikace nemusejí fungovat správně. Připomínáme, že tuto aplikaci nelze smazat, protože byla v zařízení předinstalována. Deaktivováním ji vypnete a skryjete."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Správce oprávnění"</string>
diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml
index 578122dc2..b9c2189a1 100644
--- a/PermissionController/res/values-da/strings.xml
+++ b/PermissionController/res/values-da/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Apptilladelser"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps, du ikke bruger"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ingen ubrugte apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Der er 0 ubrugte apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktiver appen"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Hvis du deaktiverer denne app, kan det medføre, at Android-apps og andre apps ikke fungerer korrekt. Vær opmærksom på, at du ikke kan slette denne app, da den er forudinstalleret på din enhed. Hvis du slår appen fra, deaktiveres og skjules den på din enhed."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Tilladelsesadministrator"</string>
diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml
index 8e69efcea..6bec32e57 100644
--- a/PermissionController/res/values-de/strings.xml
+++ b/PermissionController/res/values-de/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App-Berechtigungen"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nicht verwendete Apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Keine nicht verwendeten Apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Keine nicht verwendeten Apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"App deaktivieren"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Bei Deaktivierung dieser App funktionieren Android und andere Apps möglicherweise nicht mehr ordnungsgemäß. Beachte hierbei, dass du diese App nicht löschen kannst, weil sie auf deinem Gerät vorinstalliert war. Durch die Deaktivierung schaltest du diese App ab und blendest sie auf deinem Gerät aus."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Berechtigungs­manager"</string>
diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml
index 9f3707734..e3874c069 100644
--- a/PermissionController/res/values-el/strings.xml
+++ b/PermissionController/res/values-el/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Άδειες εφαρμογών"</string>
<string name="unused_apps" msgid="2058057455175955094">"Εφαρ. που δεν χρησιμοποιούνται"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Όλες οι εφαρμογές χρησ/νται"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 εφαρμογές που δεν χρησ/νται"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Απενεργοποίηση εφαρμογής"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Εάν απενεργοποιήσετε αυτήν την εφαρμογή, η λειτουργία του Android και άλλων εφαρμογών ενδέχεται να μην είναι η αναμενόμενη. Λάβετε υπόψη ότι δεν είναι δυνατή η διαγραφή αυτής της εφαρμογής καθώς ήταν προεγκατεστημένη στη συσκευή σας. Με την απενεργοποίηση, απενεργοποιείτε αυτήν την εφαρμογή και την αποκρύπτετε στη συσκευή σας."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Διαχείριση αδειών"</string>
diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml
index a9e341929..0150cbb25 100644
--- a/PermissionController/res/values-en-rAU/strings.xml
+++ b/PermissionController/res/values-en-rAU/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disable app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Remember that you can’t delete this app as it came pre-installed on your device. By disabling it, you turn this app off and hide it on your device."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Permission manager"</string>
diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml
index a9e341929..0150cbb25 100644
--- a/PermissionController/res/values-en-rCA/strings.xml
+++ b/PermissionController/res/values-en-rCA/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disable app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Remember that you can’t delete this app as it came pre-installed on your device. By disabling it, you turn this app off and hide it on your device."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Permission manager"</string>
diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml
index f6252c0e9..4b86fc421 100644
--- a/PermissionController/res/values-en-rGB/strings.xml
+++ b/PermissionController/res/values-en-rGB/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disable app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Remember that you can’t delete this app as it came pre-installed on your device. By disabling it, you turn this app off and hide it on your device."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Permission manager"</string>
diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml
index f6252c0e9..4b86fc421 100644
--- a/PermissionController/res/values-en-rIN/strings.xml
+++ b/PermissionController/res/values-en-rIN/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
<string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disable app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Remember that you can’t delete this app as it came pre-installed on your device. By disabling it, you turn this app off and hide it on your device."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Permission manager"</string>
diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml
index 70f70b692..ac30f905d 100644
--- a/PermissionController/res/values-en-rXC/strings.xml
+++ b/PermissionController/res/values-en-rXC/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎App permissions‎‏‎‎‏‎"</string>
<string name="unused_apps" msgid="2058057455175955094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎Unused apps‎‏‎‎‏‎"</string>
<string name="no_unused_apps" msgid="12809387670415295">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎No unused apps‎‏‎‎‏‎"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎0 unused apps‎‏‎‎‏‎"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎Disable app‎‏‎‎‏‎"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎If you disable this app, Android and other apps may no longer function as intended. Keep in mind, you can’t delete this app since it came pre-installed on your device. By disabling, you turn this app off and hide it on your device.‎‏‎‎‏‎"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎Permission manager‎‏‎‎‏‎"</string>
diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml
index 2fd8202c5..e98df991d 100644
--- a/PermissionController/res/values-es-rUS/strings.xml
+++ b/PermissionController/res/values-es-rUS/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permisos de la app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps que no usas"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No hay ninguna app sin usar"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 apps en desuso"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Inhabilitar app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si inhabilitas esta app, es posible que Android y otras apps ya no funcionen correctamente. Ten en cuenta que no puedes borrar esta app, ya que vino preinstalada en el dispositivo. Si la inhabilitas, se desactivará y se ocultará en tu dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Administrador de permisos"</string>
diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml
index 772dd7d0c..9f65c6539 100644
--- a/PermissionController/res/values-es/strings.xml
+++ b/PermissionController/res/values-es/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permisos de aplicaciones"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicaciones no usadas"</string>
<string name="no_unused_apps" msgid="12809387670415295">"No hay aplicaciones no usadas"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicaciones no usadas"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Inhabilitar aplicación"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si inhabilitas esta aplicación, es posible que Android y otras aplicaciones no funcionen como deberían. Recuerda que no puedes eliminar esta aplicación, ya que estaba preinstalada en tu dispositivo. Al inhabilitarla, desactivas la aplicación y la ocultas en tu dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestor de permisos"</string>
diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml
index 8371fe17a..b29677992 100644
--- a/PermissionController/res/values-et/strings.xml
+++ b/PermissionController/res/values-et/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Rakenduse load"</string>
<string name="unused_apps" msgid="2058057455175955094">"Kasutamata rakendused"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Kasutamata rakendusi pole"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 kasutamata rakendust"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Keela rakendus"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Selle rakenduse keelamisel ei pruugi Android ja muud rakendused enam ootuspäraselt töötada. Pange tähele, et seda rakendust ei saa kustutada, kuna see on seadmesse eelinstallitud. Rakenduse keelamisel lülitatakse see välja ja peidetakse seadmes."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Lubade haldur"</string>
diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml
index e0eda930e..c29431f2b 100644
--- a/PermissionController/res/values-eu/strings.xml
+++ b/PermissionController/res/values-eu/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Aplikazio-baimenak"</string>
<string name="unused_apps" msgid="2058057455175955094">"Erabiltzen ez diren aplikazioak"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ez dago erabiltzen ez duzun aplikaziorik"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Erabiltzen ez diren 0 aplikazio"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desgaitu aplikazioa"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Aplikazioa desgaitzen baduzu, baliteke Android-ek eta beste aplikazio batzuek behar bezala ez funtzionatzea. Kontuan izan ezin duzula ezabatu aplikazio hau gailuan berez instalatuta zetorrelako. Desgaituz gero, aplikazioa desaktibatu egingo duzu, eta gailutik ezkutatuko."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Baimenen kudeatzailea"</string>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index f4e1383b0..3312688e4 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"اجازه‌های برنامه"</string>
<string name="unused_apps" msgid="2058057455175955094">"برنامه‌های استفاده‌نشده"</string>
<string name="no_unused_apps" msgid="12809387670415295">"برنامه استفاده‌نشده‌ای موجود نیست"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"برنامه استفاده‌نشده‌ای وجود ندارد"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"غیرفعال کردن برنامه"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"‏اگر این برنامه را غیرفعال کنید، ممکن است Android و سایر برنامه‌های دیگر عملکرد موردانتظار را نداشته باشند. به‌خاطر داشته باشید که نمی‌توانید این برنامه را حذف کنید، چون از برنامه‌های ازپیش نصب‌شده روی دستگاه است. این برنامه، با غیرفعال کردن، خاموش می‌شود و در دستگاه پنهان می‌شود."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"مدیر اجازه‌ها"</string>
diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml
index b57cefb78..6be058b3d 100644
--- a/PermissionController/res/values-fi/strings.xml
+++ b/PermissionController/res/values-fi/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Sovellusluvat"</string>
<string name="unused_apps" msgid="2058057455175955094">"Käyttämättömät sovellukset"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ei käyttämättömiä sovelluksia"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 käyttämätöntä sovellusta"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Poista sovellus käytöstä"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jos poistat sovelluksen käytöstä, Android ja muut sovellukset eivät välttämättä enää toimi oikein. Muista, ettet voi poistaa sovellusta, sillä se tuli laitteesi mukana. Poistamalla sovelluksen käytöstä suljet sen ja piilotat sen laitteella."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Lupien ylläpito"</string>
diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml
index e05f384de..ee57767af 100644
--- a/PermissionController/res/values-fr-rCA/strings.xml
+++ b/PermissionController/res/values-fr-rCA/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string>
<string name="unused_apps" msgid="2058057455175955094">"Applications non utilisées"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Aucune application inutilisée"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Aucune application inutilisée"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'application"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette application, Android et d\'autres applications risquent de ne plus fonctionner correctement. Gardez à l\'esprit que vous ne pouvez pas supprimer cette application, étant donné qu\'elle était préinstallée sur votre appareil. En la désactivant, elle ne sera plus active et elle sera masquée sur votre appareil."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestionnaire des autorisations"</string>
diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml
index 879bf2b90..03e2478db 100644
--- a/PermissionController/res/values-fr/strings.xml
+++ b/PermissionController/res/values-fr/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Autorisations des applications"</string>
<string name="unused_apps" msgid="2058057455175955094">"Applications inutilisées"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Aucune appli inutilisée"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 appli inutilisée"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Désactiver l\'application"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Si vous désactivez cette application, il est possible qu\'Android et d\'autres applications ne fonctionnent plus comme prévu. N\'oubliez pas que vous ne pouvez pas supprimer cette application, car elle est préinstallée sur votre appareil. Si vous la désactivez, elle ne sera plus visible sur votre appareil."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestionnaire d\'autorisations"</string>
diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml
index 81f0de5d4..bed25e9de 100644
--- a/PermissionController/res/values-gl/strings.xml
+++ b/PermissionController/res/values-gl/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permisos de aplicacións"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicacións que non se usan"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Non hai aplicacións sen usar"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicacións que non se usan"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desactivar aplicación"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se desactivas esta aplicación, Android e outras aplicacións poden deixar de funcionar segundo o previsto. Ten en conta que esta aplicación non se pode desinstalar, xa que viña preinstalada no dispositivo. Se a desactivas, ocultarase no dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Xestor de permisos"</string>
diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml
index 19fd099da..2b5922baa 100644
--- a/PermissionController/res/values-gu/strings.xml
+++ b/PermissionController/res/values-gu/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ઍપ પરવાનગીઓ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ન વપરાયેલી ઍપ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"કોઈ બિનવપરાયેલી ઍપ નથી"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"બિનવપરાયેલી 0 ઍપ"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ઍપ બંધ કરો"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"જો તમે આ ઍપ બંધ કરશો, તો Android અને અન્ય ઍપ અપેક્ષા પ્રમાણે કાર્ય કરી શકશે નહીં. યાદ રાખો, તમે આ ઍપને ડિલીટ નહીં કરી શકો, કારણ કે તે તમારા ડિવાઇસ સાથે ઇન્સ્ટૉલ થયેલી આવી હતી. બંધ કરીને, તમે આ ઍપને માત્ર બંધ કરો છો અને તમારા ડિવાઇસમાં છુપાવો છો."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"પરવાનગી મેનેજર"</string>
diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml
index b3bc20ae9..570bbfec6 100644
--- a/PermissionController/res/values-hi/strings.xml
+++ b/PermissionController/res/values-hi/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ऐप्लिकेशन की अनुमतियां"</string>
<string name="unused_apps" msgid="2058057455175955094">"इस्तेमाल नहीं किए गए ऐप्लिकेशन"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ऐसा कोई ऐप्लिकेशन नहींं है जिसका इस्तेमाल न किया गया हो"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"इस्तेमाल न किए जाने वाले ऐप"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ऐप्लिकेशन बंद करें"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"अगर आप इस ऐप्लिकेशन को बंद कर देते हैं, तो हो सकता है कि Android और दूसरे ऐप्लिकेशन ठीक से काम न करें. ध्यान रखें कि आप इस ऐप्लिकेशन को मिटा नहीं सकते, क्योंकि यह आपके डिवाइस पर पहले से इंस्टॉल होकर आया है. इसे बंद करने पर ऐप्लिकेशन बंद हो जाएगा और आपके डिवाइस पर नहीं दिखेगा."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"अनुमतियों को मैनेज करना"</string>
diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml
index bcd54275c..9649db787 100644
--- a/PermissionController/res/values-hr/strings.xml
+++ b/PermissionController/res/values-hr/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Dopuštenja za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nekorištene aplikacije"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nema nekorištenih aplikacija"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Nema nekorištenih aplikacija"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Onemogući aplikaciju"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ako onemogućite tu aplikaciju, Android i druge aplikacije možda više neće funkcionirati pravilno. Napominjemo da ne možete izbrisati tu aplikaciju jer je predinstalirana na vašem uređaju. Ako je onemogućite, isključit ćete je i više se neće prikazivati na vašem uređaju."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Upravitelj dopuštenja"</string>
diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml
index 35f0c021b..84a8346a1 100644
--- a/PermissionController/res/values-hu/strings.xml
+++ b/PermissionController/res/values-hu/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Alkalmazásengedélyek"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nem használt alkalmazások"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nincsenek nem használt appok"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 nem használt alkalmazás"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Alkalmazás letiltása"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ha letiltja ezt az alkalmazást, akkor előfordulhat, hogy az Android- és más alkalmazások nem működnek majd megfelelően. Ne feledje, hogy az alkalmazást nem törölheti, mert előre telepítették az eszközre. A letiltással kikapcsolja ezt az alkalmazást, és elrejti az eszközön."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Engedélykezelő"</string>
diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml
index 3931af6f1..0016aabaf 100644
--- a/PermissionController/res/values-hy/strings.xml
+++ b/PermissionController/res/values-hy/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Հավելվածների թույլտվություններ"</string>
<string name="unused_apps" msgid="2058057455175955094">"Չօգտագործվող հավելվածներ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Չօգտագործվող հավելվածներ չկան"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Չօգտագործվող հավելվածներ չկան"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Անջատել հավելվածը"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Եթե անջատեք այս հավելվածը, համակարգը և այլ հավելվածներ հնարավոր է սխալներով աշխատեն։ Դուք չեք կարող ջնջել այս հավելվածը, քանի որ այն նախապես տեղադրված է եղել ձեր սարքում։ Անջատելու դեպքում հավելվածը կդադարի աշխատել և կթաքցվի։"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Թույլտվությունների կառավարիչ"</string>
diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml
index a32a897f1..2105b70a8 100644
--- a/PermissionController/res/values-in/strings.xml
+++ b/PermissionController/res/values-in/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Izin aplikasi"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikasi tidak digunakan"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Tak ada apl yang tidak dipakai"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikasi tidak digunakan"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Nonaktifkan aplikasi"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jika Anda menonaktifkan aplikasi ini, Android dan aplikasi lain mungkin tidak berfungsi lagi sesuai harapan. Perlu diingat, Anda tidak dapat menghapus aplikasi yang disertakan oleh pabrikan di perangkat Anda. Namun, Anda dapat menonaktifkannya, yang berarti mematikan dan menyembunyikan aplikasi tersebut di perangkat Anda."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Pengelola izin"</string>
diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml
index ec172505d..8083950d2 100644
--- a/PermissionController/res/values-is/strings.xml
+++ b/PermissionController/res/values-is/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Heimildir forrits"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ónotuð forrit"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Engin ónotuð forrit"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ónotuð forrit"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Slökkva á forriti"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ef þú slekkur á þessu forriti getur verið að Android og önnur forrit virki ekki lengur sem skyldi. Hafðu í huga að þú getur ekki eytt þessu forriti þar sem það var foruppsett í tækinu þínu. Ef þú gerir forritið óvirkt slekkur þú á því og felur það í tækinu."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Heimildastjóri"</string>
diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml
index f9c337448..7c9805b5a 100644
--- a/PermissionController/res/values-it/strings.xml
+++ b/PermissionController/res/values-it/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Autorizzazioni app"</string>
<string name="unused_apps" msgid="2058057455175955094">"App inutilizzate"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nessuna app inutilizzata"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 app inutilizzate"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disattiva app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se disattivi questa app, Android e altre app potrebbero non funzionare più come previsto. Tieni presente che non puoi eliminare questa app perché è preinstallata sul tuo dispositivo. Puoi scegliere di disattivare l\'app e nasconderla sul tuo dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestione autorizzazioni"</string>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index 663c9ac35..afb792511 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"הרשאות לאפליקציות"</string>
<string name="unused_apps" msgid="2058057455175955094">"אפליקציות שמזמן לא השתמשת בהן"</string>
<string name="no_unused_apps" msgid="12809387670415295">"אין אפליקציות שאינן בשימוש"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"אין אפליקציות שאינן בשימוש"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"השבתת האפליקציה"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"‏אם האפליקציה הזו תושבת, ייתכן ש-Android ואפליקציות אחרות לא יפעלו כצפוי. חשוב לזכור שלא ניתן למחוק את האפליקציה, כי היא הותקנה מראש במכשיר. ההשבתה מכבה את האפליקציה ומסתירה אותה במכשיר."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"מנהל ההרשאות"</string>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index 9561c3d3e..6dcbe3e9c 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"アプリの権限"</string>
<string name="unused_apps" msgid="2058057455175955094">"使用されていないアプリ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"使用されていないアプリなし"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"使用していないアプリ: 0 個"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"アプリを無効にする"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"このアプリを無効にすると、Android などの他のアプリが正しく動作しなくなるおそれがあります。このアプリはデバイスにプリインストールされているため、削除できません。無効にするには、このアプリをオフにし、デバイスにアプリが表示されないようにします。"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"権限マネージャー"</string>
diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml
index 42443feea..06d37d8e9 100644
--- a/PermissionController/res/values-ka/strings.xml
+++ b/PermissionController/res/values-ka/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"აპის ნებართვები"</string>
<string name="unused_apps" msgid="2058057455175955094">"გამოუყენებელი აპები"</string>
<string name="no_unused_apps" msgid="12809387670415295">"გამოუყენებელი აპები არ არის"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 გამოუყენებელი აპი"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"აპის გათიშვა"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ამ აპის გათიშვის შემთხვევაში, Android-მა და სხვა აპებმა შეიძლება გამართულად აღარ იმუშაოს. გაითვალისწინეთ, რომ ამ აპს ვერ წაშლით, რადგან ის მოწყობილობაზე წინასწარ იყო ინსტალირებული. გათიშვით, თქვენ გამორთავთ ამ აპს და დამალავთ მოწყობილობაზე."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ნებართვების მმართველი"</string>
diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml
index dd3c7bc1e..d88e9e00d 100644
--- a/PermissionController/res/values-kk/strings.xml
+++ b/PermissionController/res/values-kk/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Қолданба рұқсаттары"</string>
<string name="unused_apps" msgid="2058057455175955094">"Пайдаланылмайтын қолданбалар"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Пайдаланылмайтын қолданбалар жоқ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 пайдаланылмайтын қолданба бар"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Қолданбаны өшіру"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Бұл қолданбаны өшірсеңіз, Android жүйесі мен басқа қолданбалар тиісінше жұмыс істемеуі мүмкін. Бұл қолданба құрылғыға алдын ала орнатылатындықтан, оны жою мүмкін еместігін ескеріңіз. Өшіріп қою арқылы сіз бұл қолданбаны ажыратып, оны құрылғыда жасыра аласыз."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Рұқсат менеджері"</string>
diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml
index 9ce3517c9..b9f926324 100644
--- a/PermissionController/res/values-km/strings.xml
+++ b/PermissionController/res/values-km/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ការអនុញ្ញាតកម្មវិធី"</string>
<string name="unused_apps" msgid="2058057455175955094">"កម្មវិធី​ដែលមិន​ប្រើ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"មិនមានកម្មវិធីដែលមិនបានប្រើទេ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"កម្មវិធី​ដែលមិន​ប្រើ 0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"បិទកម្មវិធី"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ប្រសិន​បើ​អ្នក​បិទ​កម្មវិធីនេះ កម្មវិធី Android និង​កម្មវិធី​ផ្សេង​ទៀត​អាច​នឹង​លែង​ដំណើរការដូច​ដែលអ្វី​​ចង់បាន​។ សូមចាំថា អ្នក​មិនអាច​លុបកម្មវិធី​នេះ​បាន​ទេ ដោយសារកម្មវិធី​នេះត្រូវបាន​ដំឡើងជាស្រេច​នៅលើឧបករណ៍​របស់អ្នក។ ប្រសិនបើ​បិទ មានន័យថាអ្នក​បិទកម្មវិធីនេះ ហើយលាក់វានៅលើឧបករណ៍​របស់អ្នក។"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"កម្មវិធីគ្រប់គ្រង​ការអនុញ្ញាត"</string>
diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml
index 4b1eb860a..61a51ee3d 100644
--- a/PermissionController/res/values-kn/strings.xml
+++ b/PermissionController/res/values-kn/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ಆ್ಯಪ್ ಅನುಮತಿಗಳು"</string>
<string name="unused_apps" msgid="2058057455175955094">"ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ಯಾವುದೇ ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳಿಲ್ಲ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ಬಳಕೆಯಾಗದ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ಆ್ಯಪ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದರೆ, Android ಮತ್ತು ಇತರ ಆ್ಯಪ್‌ಗಳು ಇನ್ನು ಮುಂದೆ ಉದ್ದೇಶಿಸಿದ ಹಾಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರಬಹುದು. ಈ ಆ್ಯಪ್ ಅನ್ನು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪೂರ್ವ-ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿರುವುದರಿಂದ, ನೀವು ಅದನ್ನು ಅಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುವ ಮೂಲಕ, ಈ ಆ್ಯಪ್ ಅನ್ನು ನೀವು ಆಫ್ ಮಾಡಬಹುದು ಮತ್ತು ಅದನ್ನು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಮರೆ ಮಾಡಬಹುದು."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ಅನುಮತಿ ನಿರ್ವಾಹಕ"</string>
diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml
index 04f716380..b1094424b 100644
--- a/PermissionController/res/values-ko/strings.xml
+++ b/PermissionController/res/values-ko/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"앱 권한"</string>
<string name="unused_apps" msgid="2058057455175955094">"사용하지 않는 앱"</string>
<string name="no_unused_apps" msgid="12809387670415295">"사용하지 않는 앱 없음"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"사용하지 않는 앱 0개"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"앱 사용 중지"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"앱을 사용 중지하면 Android와 다른 앱이 제대로 작동하지 않을 수도 있습니다. 또한 기기에 사전 설치된 앱이므로 삭제할 수 없습니다. 비활성화하면 앱이 사용 중지되고 기기에서 숨겨집니다."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"권한 관리자"</string>
diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml
index d5f26ee6c..fb9f60105 100644
--- a/PermissionController/res/values-ky/strings.xml
+++ b/PermissionController/res/values-ky/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Колдонмонун уруксаттары"</string>
<string name="unused_apps" msgid="2058057455175955094">"Колдонулбаган колдонмолор"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Бардык колдонмолор иштетилүүдө"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Колдонулбаган колдонмолор: 0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Колдонмону өчүрүү"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Эгер бул колдонмону өчүрсөңүз, Android жана башка колдонмолор талаптагыдай иштебей калышы мүмкүн. Бул колдонмо түзмөгүңүздө алдын ала орнотулуп келген, андыктан аны өчүрө албайсыз. Өчүрүү менен, бул түзмөктү өчүрүп, түзмөгүңүздө жашырасыз."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Уруксаттарды башкаргыч"</string>
diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml
index 6a0e99bf4..0a226a5a4 100644
--- a/PermissionController/res/values-lo/strings.xml
+++ b/PermissionController/res/values-lo/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ສິດອະນຸຍາດແອັບ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ບໍ່ມີແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ແອັບທີ່ບໍ່ໄດ້ໃຊ້"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ປິດນຳໃຊ້ແອັບ"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ຖ້າທ່ານປິດນຳໃຊ້ແອັບນີ້, Android ແລະ ແອັບອື່ນອາດບໍ່ເຮັດວຽກຕາມທີ່ຖືກອອກແບບມາໄດ້. ກະລຸນາຮັບຊາບວ່າ: ທ່ານບໍ່ສາມາດລຶບແອັບນີ້ເນື່ອງຈາກມັນຖືກຕິດຕັ້ງມາພ້ອມກັບອຸປະກອນທ່ານ. ໂດຍການປິດການນຳໃຊ້, ທ່ານຈະປິດແອັບນີ້ໄວ້ ແລະ ເຊື່ອງມັນຈາກອຸປະກອນຂອງທ່ານ."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ຕົວຈັດການສິດອະນຸຍາດ"</string>
diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml
index f968d0ae3..2e3d7f241 100644
--- a/PermissionController/res/values-lt/strings.xml
+++ b/PermissionController/res/values-lt/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Programų leidimai"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nenaudojamos programos"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nėra nenaudojamų programų"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Nenaudojamų programų: 0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Išjungti programą"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jei išjungsite šią programą, „Android“ ir kitos programos gali nebeveikti, kaip numatyta. Atminkite, kad negalite ištrinti šios programos, nes ji buvo iš anksto įdiegta įrenginyje. Išjungę išjungsite šią programą ir paslėpsite ją įrenginyje."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Leidimų tvarkytuvė"</string>
diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml
index f61c25b5a..c957b7ceb 100644
--- a/PermissionController/res/values-lv/strings.xml
+++ b/PermissionController/res/values-lv/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Lietotņu atļaujas"</string>
<string name="unused_apps" msgid="2058057455175955094">"Neizmantotās lietotnes"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nav neizmantotu lietotņu"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 neizmantotu lietotņu"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Atspējot lietotni"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ja atspējosiet šo lietotni, Android un citas lietotnes, iespējams, vairs nedarbosies, kā paredzēts. Ņemiet vērā, ka šo lietotni nevar izdzēst, jo tā bija iepriekš instalēta ierīcē. Ja atspējosiet lietotni, tā tiks izslēgta un paslēpta jūsu ierīcē."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Atļauju pārvaldnieks"</string>
diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml
index 57f5374e5..6314daa31 100644
--- a/PermissionController/res/values-mk/strings.xml
+++ b/PermissionController/res/values-mk/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Дозволи за апликацијата"</string>
<string name="unused_apps" msgid="2058057455175955094">"Некористени апликации"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Нема некористени апликации"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 некористени апликации"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Оневозможи ја апликацијата"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ако ја оневозможите оваа апликација, Android и другите апликации можеби веќе нема да функционираат како што треба. Имајте предвид, не може да ја избришете апликацијава бидејќи е однапред инсталирана на уредот. Ако ја оневозможите, ќе ја исклучите и ќе ја сокриете на уредот."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Управувач со дозволи"</string>
diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml
index 1d5bbb2b0..dc01f67f6 100644
--- a/PermissionController/res/values-ml/strings.xml
+++ b/PermissionController/res/values-ml/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ആപ്പ് അനുമതികൾ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ഉപയോഗിക്കാത്ത ആപ്പുകൾ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ഉപയോഗിക്കാത്ത ആപ്പുകൾ ഇല്ല"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"ഉപയോഗിക്കാത്ത 0 ആപ്പുകൾ"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ആപ്പ് പ്രവർത്തനരഹിതമാക്കുക"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"നിങ്ങൾ ഈ ആപ്പ് പ്രവർത്തനരഹിതമാക്കിയാൽ, Android-ഉം മറ്റ് ആപ്പുകളും ഇനി ഉദ്ദേശിച്ചത് പോലെ പ്രവർത്തിച്ചേക്കില്ല. ശ്രദ്ധിക്കുക, നിങ്ങളുടെ ഉപകരണത്തിൽ മുമ്പേ ഇൻസ്‌റ്റാൾ ചെയ്‌തതായതിനാൽ ഈ ആപ്പ് നിങ്ങൾക്ക് ഇല്ലാതാക്കാനാവില്ല. പ്രവർത്തനരഹിതമാക്കുന്നത് വഴി, ഈ ആപ്പ് ഓഫാക്കി നിങ്ങൾക്കിത് ഉപകരണത്തിൽ മറച്ചു വയ്ക്കാം."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"അനുമതി മാനേജർ"</string>
diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml
index 166fe7bcd..bb81c4cff 100644
--- a/PermissionController/res/values-mn/strings.xml
+++ b/PermissionController/res/values-mn/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Аппын зөвшөөрөл"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ашиглаагүй аппууд"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ашиглаагүй апп байхгүй байна"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ашиглаагүй апп"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Aппыг идэвхгүй болгох"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Хэрэв та энэ аппыг идэвхгүй болговол Android болон бусад апп цаашид хэвийн ажиллахгүй байж болзошгүй. Энэ аппыг таны төхөөрөмжид урьдчилан суулгасан тул устгах боломжгүйг анхаарна уу. Идэвхгүй болгосноор та энэ аппыг унтрааж, төхөөрөмж дээрээсээ нуух болно."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Зөвшөөрлийн менежер"</string>
diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml
index d24f03047..34b8e73b1 100644
--- a/PermissionController/res/values-mr/strings.xml
+++ b/PermissionController/res/values-mr/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"अ‍ॅप परवानग्या"</string>
<string name="unused_apps" msgid="2058057455175955094">"न वापरलेली अ‍ॅप्स"</string>
<string name="no_unused_apps" msgid="12809387670415295">"न वापरलेली कोणतीही ॲप्स नाहीत"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"न वापरलेली शून्य अ‍ॅप्स"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"अ‍ॅप बंद करा"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"तुम्ही हे अ‍ॅप बंद केल्यास, Android आणि इतर अ‍ॅप्स कदाचित अपेक्षेप्रमाणे काम करणार नाहीत. लक्षात ठेवा, हे अ‍ॅप तुमच्या डिव्हाइसवर आधीच इंस्टॉल करून आलेले असल्यामुळे तुम्ही ते हटवू शकत नाही. बंद करून, तुम्ही हे अ‍ॅप बंद करता आणि तुमच्या डिव्हाइसवर ते लपवता."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"परवानगी व्यवस्थापक"</string>
diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml
index 7466ec433..8f053aa89 100644
--- a/PermissionController/res/values-ms/strings.xml
+++ b/PermissionController/res/values-ms/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Kebenaran apl"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apl yang tidak digunakan"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Tiada apl yang tidak digunakan"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 apl yang tidak digunakan"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Lumpuhkan apl"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jika anda melumpuhkan apl ini, Android dan apl lain mungkin tidak berfungsi seperti yang sepatutnya. Jangan lupa, anda tidak boleh memadamkan apl ini kerana apl diprapasang pada peranti anda. Dengan melumpuhkan apl, anda mematikan dan menyembunyikan apl ini pada peranti anda."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Pengurus kebenaran"</string>
diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml
index 980d93ba1..e279c6a1c 100644
--- a/PermissionController/res/values-my/strings.xml
+++ b/PermissionController/res/values-my/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"အက်ပ်ခွင့်ပြုချက်များ"</string>
<string name="unused_apps" msgid="2058057455175955094">"အသုံးမပြုသော အက်ပ်များ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"အသုံးမပြုသောအက်ပ်များ မရှိပါ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"အသုံးမပြုသောအက်ပ် 0 ခု"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"အက်ပ်ကို ပိတ်ရန်"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ဤအက်ပ်ကို ပိတ်လိုက်လျှင် Android နှင့် အခြားအက်ပ်များ ပုံမှန် အလုပ်လုပ်တော့မည် မဟုတ်ပါ။ ဤအက်ပ်ကို သင့်စက်ပေါ်တွင် ကြိုတင်ထည့်သွင်းထားသဖြင့် ၎င်းကို ဖျက်၍မရပါ။ ပိတ်ခြင်းဖြင့် ဤအက်ပ်ကို ရပ်လိုက်ပြီး သင့်စက်ပေါ်တွင် ဖျောက်ထားပါမည်။"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ခွင့်ပြုချက် မန်နေဂျာ"</string>
diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml
index 7081bfc4a..2be34bacf 100644
--- a/PermissionController/res/values-nb/strings.xml
+++ b/PermissionController/res/values-nb/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Apptillatelser"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ubrukte apper"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ingen ubrukte apper"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ubrukte apper"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktiver appen"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Hvis du deaktiverer denne appen, slutter muligens Android og andre apper å fungere som de skal. Husk at du ikke kan slette denne appen, fordi den var forhåndsinstallert på enheten din. Ved å deaktivere appen slår du den av og skjuler den på enheten din."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Tillatelseskontroll"</string>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index 0f95aa338..f0c4b4165 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"एपसम्बन्धी अनुमति"</string>
<string name="unused_apps" msgid="2058057455175955094">"प्रयोग नगरिएका एपहरू"</string>
<string name="no_unused_apps" msgid="12809387670415295">"सबै एप चलाइएका छन्"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"प्रयोग नगरिएका एउटा पनि एप छैन"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"एप असक्षम पार्नुहोस्"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"तपाईंले यो एप असक्षम पार्नुभयो भने Android र अन्य एपहरूले अब उप्रान्त अपेक्षाअनुसार कार्य नगर्न सक्छन्। स्मरण रहोस्, तपाईं यो एप तपाईंको यन्त्रसँग पहिल्यै स्थापना भएर आएको हुँदा तपाईं यसलाई मेटाउन सक्नुहुन्न। यो एप असक्षम पारेर, तपाईं यसलाई निष्क्रिय पार्नुहुन्छ तथा यसलाई आफ्नो डिभाइसमा लुकाउनुहुन्छ।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"पर्मिसन म्यानेजर"</string>
diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml
index 184c14e32..5a3b27d3b 100644
--- a/PermissionController/res/values-nl/strings.xml
+++ b/PermissionController/res/values-nl/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"App-rechten"</string>
<string name="unused_apps" msgid="2058057455175955094">"Niet-gebruikte apps"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Geen niet-gebruikte apps"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 niet-gebruikte apps"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"App uitzetten"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Als je deze app uitzet, werken Android en andere apps mogelijk niet meer zoals bedoeld. Je kunt deze app niet verwijderen omdat deze vooraf geïnstalleerd was op je apparaat. Door de app uit te zetten verberg je deze ook op je apparaat."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Rechtenbeheer"</string>
diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml
index 83f3cb2d2..99567d3d1 100644
--- a/PermissionController/res/values-or/strings.xml
+++ b/PermissionController/res/values-or/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ଆପ୍‌ ଅନୁମତିଗୁଡ଼ିକ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ଅବ୍ୟବହୃତ ଆପଗୁଡ଼ିକ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"କୌଣସି ଅବ୍ୟବହୃତ ଆପ୍ ନାହିଁ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0ଟି ଅବ୍ୟବହୃତ ଆପ୍"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ଆପ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ଯଦି ଆପଣ ଏହି ଆପକୁ ଅକ୍ଷମ କରିବେ, ତେବେ Android ଓ ଅନ୍ୟ ଆପଗୁଡ଼ିକ ଆଉ ଆଶାନୁରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ। ମନେରଖନ୍ତୁ ଯେ, ଏହି ଆପ୍ ଆପଣଙ୍କ ଡିଭାଇସରେ ପୂର୍ବରୁ-ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ଯୋଗୁଁ ଆପଣ ଏହାକୁ ଡିଲିଟ୍ କରିପାରିବେ ନାହିଁ। ଅକ୍ଷମ କରି, ଆପଣ ଏହି ଆପକୁ ବନ୍ଦ କରିଦିଅନ୍ତି ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସରେ ଲୁଚାଇଦିଅନ୍ତି।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ଅନୁମତି ପରିଚାଳକ"</string>
diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml
index 4f4792334..4693068e0 100644
--- a/PermissionController/res/values-pa/strings.xml
+++ b/PermissionController/res/values-pa/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ਐਪ ਇਜਾਜ਼ਤਾਂ"</string>
<string name="unused_apps" msgid="2058057455175955094">"ਅਣਵਰਤੀਆਂ ਐਪਾਂ"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ਕੋਈ ਅਣਵਰਤੀ ਐਪ ਨਹੀਂ"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ਅਣਵਰਤੀਆਂ ਐਪਾਂ"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ਐਪ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਬੰਦ ਕਰਦੇ ਹੋ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ Android ਅਤੇ ਹੋਰ ਐਪਾਂ ਉਸ ਤਰ੍ਹਾਂ ਕੰਮ ਨਾ ਕਰਨ ਜਿਵੇਂ ਇਹਨਾਂ ਤੋਂ ਉਮੀਦ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ, ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਵਿੱਚ ਇਹ ਐਪ ਪਹਿਲਾਂ ਤੋਂ ਸਥਾਪਤ ਮਿਲੀ ਹੋਣ ਕਰਕੇ ਤੁਸੀਂ ਇਸ ਨੂੰ ਮਿਟਾ ਨਹੀਂ ਸਕਦੇ। ਬੰਦ ਕਰਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਇਹ ਐਪ ਬੰਦ ਕਰ ਅਤੇ ਲੁਕਾ ਰਹੇ ਹੋ।"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"ਇਜਾਜ਼ਤ ਪ੍ਰਬੰਧਕ"</string>
diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml
index 6fe040eb9..0caa93093 100644
--- a/PermissionController/res/values-pl/strings.xml
+++ b/PermissionController/res/values-pl/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Uprawnienia aplikacji"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nieużywane aplikacje"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Brak nieużywanych aplikacji"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 nieużywanych aplikacji"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Wyłącz aplikację"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Jeśli wyłączysz tę aplikację, Android i inne aplikacje mogą działać nieprawidłowo. Pamiętaj, że nie możesz usunąć tej aplikacji, bo została ona fabrycznie zainstalowana na Twoim urządzeniu. Wyłączone aplikacje są ukrywane na urządzeniu."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Menedżer uprawnień"</string>
diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml
index 1ebbc21f5..dabe83be9 100644
--- a/PermissionController/res/values-pt-rBR/strings.xml
+++ b/PermissionController/res/values-pt-rBR/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permissões do app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não usados"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhum app não usado"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 app não usado"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desativar app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se você desativar este app, o Android e outros apps poderão deixar de funcionar como deveriam. Não é possível excluir esse app porque ele veio pré-instalado no seu dispositivo. Ao optar pela desativação, você desativa o app e o oculta no dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gerenciador de permissões"</string>
diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml
index eb1d3d2f8..6e18090a9 100644
--- a/PermissionController/res/values-pt-rPT/strings.xml
+++ b/PermissionController/res/values-pt-rPT/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Autorizações da app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não utilizadas"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhuma app não utilizada"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 apps não utilizadas"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desativar app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se desativar esta app, o Android e outras apps podem deixar de funcionar corretamente. Não se esqueça de que não pode eliminar esta app por ter sido pré-instalada no dispositivo. Ao desativá-la, irá ocultá-la no dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gestor de autorizações"</string>
diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml
index 1ebbc21f5..dabe83be9 100644
--- a/PermissionController/res/values-pt/strings.xml
+++ b/PermissionController/res/values-pt/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permissões do app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Apps não usados"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nenhum app não usado"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 app não usado"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Desativar app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Se você desativar este app, o Android e outros apps poderão deixar de funcionar como deveriam. Não é possível excluir esse app porque ele veio pré-instalado no seu dispositivo. Ao optar pela desativação, você desativa o app e o oculta no dispositivo."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Gerenciador de permissões"</string>
diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml
index 86f40f1a9..e3782a01c 100644
--- a/PermissionController/res/values-ro/strings.xml
+++ b/PermissionController/res/values-ro/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Permisiuni pentru aplicații"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplicații nefolosite"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nu există aplicații nefolosite"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplicații nefolosite"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Dezactivați aplicația"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Dacă dezactivați această aplicație, este posibil ca Android și alte aplicații să nu mai funcționeze corespunzător. Rețineți că nu puteți șterge aplicația, deoarece a fost preinstalată pe dispozitiv. Dezactivând-o, opriți această aplicație și o ascundeți pe dispozitiv."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Manager de permisiuni"</string>
diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml
index 3b1011a5a..a770ad15a 100644
--- a/PermissionController/res/values-ru/strings.xml
+++ b/PermissionController/res/values-ru/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Разрешения приложений"</string>
<string name="unused_apps" msgid="2058057455175955094">"Неиспользуемые приложения"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Неиспользуемых приложений нет"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Нет неиспользуемых приложений"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Отключить приложение"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Если вы отключите это приложение, система и другие программы могут работать неправильно. Приложение установлено производителем, поэтому его нельзя удалить с устройства. После отключения оно перестанет работать и будет скрыто."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Разрешения"</string>
diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml
index 8c959d530..fd50b7b4a 100644
--- a/PermissionController/res/values-si/strings.xml
+++ b/PermissionController/res/values-si/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"යෙදුම් අවසර"</string>
<string name="unused_apps" msgid="2058057455175955094">"භාවිත නොකළ යෙදුම්"</string>
<string name="no_unused_apps" msgid="12809387670415295">"භාවිත නොකළ යෙදුම් නැත"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"භාවිත නොකළ යෙදුම් 0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"යෙදුම අබල කරන්න"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"ඔබ මෙම යෙදුම අබල කළහොත්, Android සහ වෙනත් යෙදුම් තවදුරටත් අපේක්ෂා කළ පරිදි ක්‍රියා නොකළ හැකිය. මෙය ඔබගේ උපාංගයේ පූර්ව ස්ථාපිතව පැමිණි නිසා ඔබට මෙම යෙදුම මැකිය නොහැකි බව මතක තබා ගන්න. අබල කිරීමෙන්, ඔබ මෙම යෙදුම අබල කර එය ඔබගේ උපාංගයේ සඟවයි."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"අවසර කළමනාකරු"</string>
diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml
index aff3dbaea..39768aa36 100644
--- a/PermissionController/res/values-sk/strings.xml
+++ b/PermissionController/res/values-sk/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Povolenia aplikácií"</string>
<string name="unused_apps" msgid="2058057455175955094">"Nepoužívané aplikácie"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Žiadne nepoužívané aplikácie"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Žiadne nepoužívané aplikácie"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Deaktivovať aplikáciu"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ak aplikáciu deaktivujete, systém a ďalšie aplikácie už nemusia fungovať podľa očakávaní. Upozorňujeme, že túto aplikáciu nemôžete odstrániť, pretože bola vo vašom zariadení predinštalovaná. Deaktiváciou ju vypnete a skryjete v zariadení."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Správca povolení"</string>
diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml
index cdc70ce6f..df2e3d24d 100644
--- a/PermissionController/res/values-sl/strings.xml
+++ b/PermissionController/res/values-sl/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Dovoljenja za aplikacije"</string>
<string name="unused_apps" msgid="2058057455175955094">"Neuporabljene aplikacije"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ni neuporabljenih aplikacij."</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 neuporabljenih aplikacij"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Onemogočanje aplikacije"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Če onemogočite to aplikacijo, Android in druge aplikacije morda ne bodo več delovali, kot bi morali. Upoštevajte, da te aplikacije ni mogoče izbrisati, ker je bila vnaprej nameščena v napravi. Če jo onemogočite, jo boste izklopili in skrili v napravi."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Upravitelj dovoljenj"</string>
diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml
index aa17a9411..a4d69bef5 100644
--- a/PermissionController/res/values-sq/strings.xml
+++ b/PermissionController/res/values-sq/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Lejet e aplikacionit"</string>
<string name="unused_apps" msgid="2058057455175955094">"Aplikacionet e papërdorura"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Nuk ka aplikacione të papërdorura"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 aplikacione të papërdorura"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Çaktivizo aplikacionin"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Nëse e çaktivizon këtë aplikacion, Android dhe aplikacionet e tjera mund të mos funksionojnë më siç pritet. Ki parasysh se nuk mund ta fshish këtë aplikacion pasi ai ka ardhur i instaluar paraprakisht në pajisjen tënde. Duke e çaktivizuar, mund ta kalosh këtë aplikacion në joaktiv dhe ta fshehësh në pajisjen tënde."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Menaxheri i lejeve"</string>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index 0b18eb211..7de9aa3f3 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Дозволе за апликације"</string>
<string name="unused_apps" msgid="2058057455175955094">"Апликације које се не користе"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Нема аплик. које се не користе"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 апликац. које се не користе"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Онемогући апликацију"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ако онемогућите ову апликацију, Android и друге апликације можда више неће радити исправно. Имајте на уму да не можете да избришете ову апликацију јер је била унапред инсталирана на уређају. Ако је онемогућите, искључићете је и сакрити на уређају."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Менаџер дозвола"</string>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index 5b5f27ad1..6e8b8ec6d 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Appbehörigheter"</string>
<string name="unused_apps" msgid="2058057455175955094">"Appar som inte används"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Inga appar som inte används"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 appar som inte används"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Inaktivera app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Om du inaktiverar den här appen kan det påverka Android och andra appar så att de inte längre fungerar som de ska. Du kan inte radera appen eftersom den har förinstallerats på enheten. Om du inaktiverar appen döljs den på enheten."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Behörighetshantering"</string>
diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml
index 517c9508f..359536e18 100644
--- a/PermissionController/res/values-sw/strings.xml
+++ b/PermissionController/res/values-sw/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Ruhusa za programu"</string>
<string name="unused_apps" msgid="2058057455175955094">"Programu zisizotumika"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Hakuna programu zisizotumika"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Hakuna programu zisizotumika"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Zima programu"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Ukizima programu hii, huenda Android na programu zingine zikakosa kufanya kazi tena kama zilivyokusudiwa. Kumbuka, huwezi kufuta programu hii kwa kuwa ilisakinishwa mapema kwenye kifaa chako. Kwa kuizima, unazima programu hii na kuificha kwenye kifaa chako."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Kidhibiti cha ruhusa"</string>
diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml
index 75cdc8350..9c2b37571 100644
--- a/PermissionController/res/values-ta/strings.xml
+++ b/PermissionController/res/values-ta/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ஆப்ஸ் அனுமதிகள்"</string>
<string name="unused_apps" msgid="2058057455175955094">"பயன்படுத்தாத ஆப்ஸ்"</string>
<string name="no_unused_apps" msgid="12809387670415295">"பயன்படுத்தாத ஆப்ஸ் எதுவுமில்லை"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 பயன்படுத்தாத ஆப்ஸ்"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ஆப்ஸை முடக்கு"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"இந்த ஆப்ஸை முடக்கினால் Android மற்றும் பிற ஆப்ஸ் முறையாக செயல்படாமல் போகக்கூடும். கவனத்திற்கு: இந்த ஆப்ஸ் சாதனத்தில் நிறுவப்பட்ட நிலையிலேயே வந்ததால் இதை நீக்க முடியாது. முடக்குவதன் மூலம் இந்த ஆப்ஸ் ஆஃப் செய்யப்படுவதோடு சாதனத்தில் மறைக்கப்படும்."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"அனுமதி நிர்வாகம்"</string>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index 914b6ade8..e299d74d8 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"యాప్ అనుమతులు"</string>
<string name="unused_apps" msgid="2058057455175955094">"ఉపయోగించని యాప్‌లు"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ఉపయోగించని యాప్‌లు లేవు"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ఉపయోగించని యాప్‌లు"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"యాప్‌ను డిజేబుల్‌ చేయి"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"మీరు ఈ యాప్‌ను డిజేబుల్‌ చేస్తే, Android మరియు ఇతర యాప్‌లు ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు. ఈ యాప్ మీ పరికరంలో ముందుగానే ఇన్‌స్టాల్ చేసి, అందించబడింది కాబట్టి మీరు దీనిని తొలగించలేరని గుర్తుంచుకోండి. డిజేబుల్‌ చేయడం ద్వారా, మీరు ఈ యాప్‌ను ఆఫ్ చేసి, మీ పరికరంలో దానిని దాస్తున్నారు."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"అనుమతి మేనేజర్"</string>
diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml
index 7e458f339..5431c500f 100644
--- a/PermissionController/res/values-th/strings.xml
+++ b/PermissionController/res/values-th/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"สิทธิ์ของแอป"</string>
<string name="unused_apps" msgid="2058057455175955094">"แอปที่ไม่ได้ใช้"</string>
<string name="no_unused_apps" msgid="12809387670415295">"ไม่มีแอปที่ไม่ได้ใช้งาน"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"แอปที่ไม่ได้ใช้ 0 รายการ"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ปิดใช้แอป"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"หากคุณปิดใช้แอปนี้ Android และแอปอื่นๆ อาจไม่ทำงานตามที่ควรจะเป็นอีกต่อไป โปรดทราบว่าคุณลบแอปนี้ไม่ได้เนื่องจากเป็นแอปที่ติดตั้งมาล่วงหน้าในอุปกรณ์ แต่การปิดใช้จะปิดแอปนี้และซ่อนในอุปกรณ์"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"เครื่องมือจัดการสิทธิ์"</string>
diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml
index 10dc50afd..edf9fa2c3 100644
--- a/PermissionController/res/values-tl/strings.xml
+++ b/PermissionController/res/values-tl/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Mga pahintulot sa app"</string>
<string name="unused_apps" msgid="2058057455175955094">"Mga hindi ginagamit na app"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Walang hindi ginagamit na app"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 hindi ginagamit na app"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"I-disable ang app"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Kung idi-disable mo ang app na ito, baka hindi na gumana ang Android at iba pang app gaya ng inaasahan. Tandaan, hindi mo made-delete ang app na ito dahil naka-preinstall ito sa iyong device. Sa pamamagitan ng pag-disable nito, io-off mo ang app na ito at itatago mo ito sa iyong device."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Manager ng pahintulot"</string>
diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml
index 07d676dd4..e48d1e477 100644
--- a/PermissionController/res/values-tr/strings.xml
+++ b/PermissionController/res/values-tr/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Uygulama izinleri"</string>
<string name="unused_apps" msgid="2058057455175955094">"Kullanılmayan uygulamalar"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Kullanılmayan uygulama yok"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Kullanılmayan 0 uygulama"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Uygulamayı devre dışı bırak"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Bu uygulamayı devre dışı bırakırsanız Android ve diğer uygulamalar artık beklendiği gibi çalışmayabilir. Cihazınızda önceden yüklü olarak geldiğinden bu uygulamayı silemeyeceğinizi unutmayın. Devre dışı bıraktığınızda bu uygulamayı kapatır ve cihazınızda gizlersiniz."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"İzin yöneticisi"</string>
diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml
index a4bd91491..61245e803 100644
--- a/PermissionController/res/values-uk/strings.xml
+++ b/PermissionController/res/values-uk/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Дозволи додатка"</string>
<string name="unused_apps" msgid="2058057455175955094">"Непотрібні додатки"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Усі додатки використовуються"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 додатків не використовуються"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Вимкнути додаток"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Якщо вимкнути цей додаток, система Android та інші додатки можуть працювати неналежним чином. Важливо: ви не можете видалити цей додаток, оскільки він був попередньо встановлений на пристрої. Вимкнений додаток буде приховано."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Диспетчер дозволів"</string>
diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml
index c24ea3b6a..813e2178b 100644
--- a/PermissionController/res/values-ur/strings.xml
+++ b/PermissionController/res/values-ur/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"ایپ کی اجازتیں"</string>
<string name="unused_apps" msgid="2058057455175955094">"غیر مستعمل ایپس"</string>
<string name="no_unused_apps" msgid="12809387670415295">"کوئی غیر مستعمل ایپ نہیں ہے"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 غیر مستعمل ایپس"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"ایپ کو غیر فعال کریں"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"‏اگر آپ اس ایپ کو غیر فعال کرتے ہیں تو ہو سکتا ہے Android اور دیگر ایپس ٹھیک سے کام نہ کریں۔ ذہن نشین رکھیں کہ آپ اس ایپ کو حذف نہیں کر سکتے ہیں کیونکہ یہ آپ کے آلہ پر پہلے سے انسٹال ہے۔ غیر فعال کر کے، آپ اس ایپ کو آف کر دیتے ہیں اور اس کو اپنے آلہ پر چھپا دیتے ہیں۔"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"اجازتوں کا مینیجر"</string>
diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml
index 8055d366a..e71535952 100644
--- a/PermissionController/res/values-uz/strings.xml
+++ b/PermissionController/res/values-uz/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Ilovalar uchun ruxsatlar"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ishlatilmagan ilovalar"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Ishlatilmagan ilovalar yoʻq"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ta ishlatilmagan ilova"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Ilovani faolsizlantirish"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Agar bu ilovani faolsizlantirsangiz, Android va boshqa ilovalar bundan buyon kutilganidek ishlamasligi mumkin. Yodda tuting, agar bu ilova qurilmangizga oldindan oʻrnatilgan boʻlsa, uni oʻchirib tashlash imkonsiz. Faolsizlantirish orqali bu ilova nofaol holatga keladi va qurilmangizda berkitiladi."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Ruxsatlar"</string>
diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml
index 1d0a5d2ef..3875b7491 100644
--- a/PermissionController/res/values-vi/strings.xml
+++ b/PermissionController/res/values-vi/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Quyền ứng dụng"</string>
<string name="unused_apps" msgid="2058057455175955094">"Ứng dụng không dùng đến"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Không có ứng dụng không dùng đến"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"0 ứng dụng không dùng đến"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Vô hiệu hóa ứng dụng"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Nếu bạn vô hiệu hóa ứng dụng này, thì Android và các ứng dụng khác có thể không hoạt động như dự kiến nữa. Lưu ý rằng bạn không thể xóa ứng dụng này vì đây là ứng dụng cài sẵn trên thiết bị. Khi bạn vô hiệu hóa, ứng dụng sẽ bị tắt và ẩn trên thiết bị."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Trình quản lý quyền"</string>
diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml
index 5cf570047..213edb1ca 100644
--- a/PermissionController/res/values-zh-rCN/strings.xml
+++ b/PermissionController/res/values-zh-rCN/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"应用权限"</string>
<string name="unused_apps" msgid="2058057455175955094">"未使用的应用"</string>
<string name="no_unused_apps" msgid="12809387670415295">"没有未使用的应用"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"没有未使用的应用"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"停用应用"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"如果您停用此应用,Android 及其他应用可能会无法再正常运行。请注意,您无法删除此应用,因为它是设备预装应用。停用后,系统将关闭此应用并在设备上将其隐藏。"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"权限管理器"</string>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 1d8b37d43..9077195b1 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"應用程式權限"</string>
<string name="unused_apps" msgid="2058057455175955094">"不使用的應用程式"</string>
<string name="no_unused_apps" msgid="12809387670415295">"沒有不使用的應用程式"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"沒有未使用的應用程式"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"停用應用程式"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"如果您停用此應用程式,Android 和其他應用程式可能無法正常運作。請謹記,由於此應用程式已在您的裝置預先安裝,因此無法刪除。停用後,您便可在裝置上關閉並隱藏此應用程式。"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"權限管理工具"</string>
diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml
index 3cda63560..635c02058 100644
--- a/PermissionController/res/values-zh-rTW/strings.xml
+++ b/PermissionController/res/values-zh-rTW/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"應用程式權限"</string>
<string name="unused_apps" msgid="2058057455175955094">"未使用的應用程式"</string>
<string name="no_unused_apps" msgid="12809387670415295">"沒有未使用的應用程式"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"沒有未使用的應用程式"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"停用應用程式"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"如果你停用這個應用程式,Android 和其他應用程式可能無法正常運作。請注意,這是預先安裝在裝置上的應用程式,因此無法刪除。不過,只要停用這個應用程式,即可將應用程式關閉並在裝置上隱藏。"</string>
<string name="app_permission_manager" msgid="3903811137630909550">"權限管理員"</string>
diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml
index a13ca4cbf..c1507827b 100644
--- a/PermissionController/res/values-zu/strings.xml
+++ b/PermissionController/res/values-zu/strings.xml
@@ -52,6 +52,7 @@
<string name="app_permissions" msgid="3369917736607944781">"Izimvume zohlelo lokusebenza"</string>
<string name="unused_apps" msgid="2058057455175955094">"Izinhlelo zokusebenza ezingasetshenzisiwe"</string>
<string name="no_unused_apps" msgid="12809387670415295">"Awekho ama-app angasetshenzisiwe"</string>
+ <string name="zero_unused_apps" msgid="9024448554157499748">"Ama-app angasetshenzisiwe angu-0"</string>
<string name="app_disable_dlg_positive" msgid="7418444149981904940">"Khubaza uhlelo lokusebenza"</string>
<string name="app_disable_dlg_text" msgid="3126943217146120240">"Uma ukhubaza lolu hlelo lokusebenza, i-Android nezinye izinhlelo zokusebenza kungenzeka zingasasebenzi njengoba zihlosiwe. Khumbula, awukwazi ukususa lolu hlelo lokusebenza njengoba luze lufakwe ngaphambilini kudivayisi yakho. Ngokukhubaza, uvala lolu hlelo lokusebenza futhi uyalifihla kudivayisi yakho."</string>
<string name="app_permission_manager" msgid="3903811137630909550">"Isiphathi semvume"</string>
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index ba03cbf63..7a086816d 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -136,6 +136,9 @@
<!-- Label when there are no unused apps [CHAR LIMIT=30] -->
<string name="no_unused_apps">No unused apps</string>
+ <!-- Label when there are zero unused apps [CHAR LIMIT=30] -->
+ <string name="zero_unused_apps">0 unused apps</string>
+
<!-- [CHAR LIMIT=30] Manage applications, label for option to disable app -->
<string name="app_disable_dlg_positive">Disable app</string>
diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml
index 04265a314..16ff3c15a 100644
--- a/PermissionController/res/values/themes.xml
+++ b/PermissionController/res/values/themes.xml
@@ -57,6 +57,10 @@
<item name="android:windowIsTranslucent">true</item>
</style>
+ <style name="GrantPermissions.Car">
+ <item name="carUiActivity">true</item>
+ </style>
+
<!-- Unused since R but exposed as overlayable. -->
<style name="Header.Settings"
parent="@android:style/Theme.DeviceDefault.Settings">
@@ -118,6 +122,10 @@
<item name="android:filterTouchesWhenObscured">true</item>
</style>
+ <style name="GrantPermissions.Car.FilterTouches">
+ <item name="android:filterTouchesWhenObscured">true</item>
+ </style>
+
<style name="RequestRole.FilterTouches">
<item name="android:filterTouchesWhenObscured">true</item>
</style>
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
index 10a58ab0a..6e0c12cc8 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
@@ -568,7 +568,8 @@ class HibernationJobService : JobService() {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
val pendingIntent = PendingIntent.getActivity(this, 0, clickIntent,
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT)
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or
+ PendingIntent.FLAG_IMMUTABLE)
var notifTitle: String
var notifContent: String
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java b/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
index f4a775270..d732732d1 100644
--- a/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
+++ b/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
@@ -244,13 +244,13 @@ class PendingList {
// Intent for the confirmation dialog.
final PendingIntent dialog = PendingIntent.getActivity(mContext, 0,
- newDialogIntent(rec), 0);
+ newDialogIntent(rec), PendingIntent.FLAG_IMMUTABLE);
// Intent for the approval and denial.
final PendingIntent deny = PendingIntent.getBroadcast(mContext, 0,
new Intent(ApprovalReceiver.ACTION_DENY, rec.report.getUri(),
mContext, ApprovalReceiver.class),
- 0);
+ PendingIntent.FLAG_IMMUTABLE);
// Construct the notification
final Notification notification = new Notification.Builder(mContext)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
index f83309fbd..b1dd404c7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.handheld.dashboard;
+package com.android.permissioncontroller.permission.model;
import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
@@ -45,10 +45,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissionUsage;
import com.android.permissioncontroller.permission.model.AppPermissionUsage.Builder;
-import com.android.permissioncontroller.permission.model.Permission;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
@@ -92,14 +89,27 @@ public final class PermissionUsages implements LoaderCallbacks<List<AppPermissio
private @Nullable PermissionsUsagesChangeCallback mCallback;
+ /**
+ * Callback for when the permission usages has loaded or changed.
+ */
public interface PermissionsUsagesChangeCallback {
+ /**
+ * Called when the permission usages have loaded or changed.
+ */
void onPermissionUsagesChanged();
}
+ /**
+ * Creates a new instance of {@link PermissionUsages}.
+ */
public PermissionUsages(@NonNull Context context) {
mContext = context;
}
+ /**
+ * Start the {@link Loader} to load the permission usages in the background. Loads without a uid
+ * filter.
+ */
public void load(@Nullable String filterPackageName,
@Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -110,6 +120,10 @@ public final class PermissionUsages implements LoaderCallbacks<List<AppPermissio
getNonPlatformPermissions, callback, sync);
}
+ /**
+ * Start the {@link Loader} to load the permission usages in the background. Loads only
+ * permissions for the specified {@code filterUid}.
+ */
public void load(int filterUid, @Nullable String filterPackageName,
@Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -155,36 +169,20 @@ public final class PermissionUsages implements LoaderCallbacks<List<AppPermissio
mCallback.onPermissionUsagesChanged();
}
+ /**
+ * Return the usages that have already been loaded.
+ */
public @NonNull List<AppPermissionUsage> getUsages() {
return mUsages;
}
+ /**
+ * Stop the {@link Loader} from loading the usages.
+ */
public void stopLoader(@NonNull LoaderManager loaderManager) {
loaderManager.destroyLoader(1);
}
- public static @Nullable AppPermissionUsage.GroupUsage loadLastGroupUsage(
- @NonNull Context context, @NonNull AppPermissionGroup group) {
- final ArraySet<String> opNames = new ArraySet<>();
- final List<Permission> permissions = group.getPermissions();
- final int permCount = permissions.size();
- for (int i = 0; i < permCount; i++) {
- final Permission permission = permissions.get(i);
- final String opName = permission.getAppOp();
- if (opName != null) {
- opNames.add(opName);
- }
- }
- final String[] opNamesArray = opNames.toArray(new String[opNames.size()]);
- final List<PackageOps> usageOps = context.getSystemService(AppOpsManager.class)
- .getOpsForPackage(group.getApp().applicationInfo.uid,
- group.getApp().packageName, opNamesArray);
- if (usageOps == null || usageOps.isEmpty()) {
- return null;
- }
- return new AppPermissionUsage.GroupUsage(group, usageOps.get(0), null);
- }
-
private static final class UsageLoader extends AsyncTaskLoader<List<AppPermissionUsage>> {
private final int mFilterUid;
private @Nullable String mFilterPackageName;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
index 76e3f33a4..6ce008396 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission_group.LOCATION;
import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.app.PendingIntent.getBroadcast;
@@ -605,7 +606,7 @@ public class LocationAccessCheck {
.setDeleteIntent(getBroadcast(mContext, 0, deleteIntent,
FLAG_ONE_SHOT | FLAG_UPDATE_CURRENT))
.setContentIntent(getBroadcast(mContext, 0, clickIntent,
- FLAG_ONE_SHOT | FLAG_UPDATE_CURRENT));
+ FLAG_ONE_SHOT | FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
if (appName != null) {
Bundle extras = new Bundle();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/AutoGrantPermissionsNotifier.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/AutoGrantPermissionsNotifier.java
index a006e4b34..8775d3916 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/AutoGrantPermissionsNotifier.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/AutoGrantPermissionsNotifier.java
@@ -16,6 +16,7 @@
package com.android.permissioncontroller.permission.ui;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.app.PendingIntent.getActivity;
import static android.content.Intent.ACTION_MANAGE_APP_PERMISSION;
@@ -166,7 +167,7 @@ public class AutoGrantPermissionsNotifier {
.setLargeIcon(pkgIconBmp)
.setColor(mContext.getColor(android.R.color.system_notification_accent_color))
.setContentIntent(getActivity(mContext, packageBasedRequestCode,
- manageAppPermission, FLAG_UPDATE_CURRENT));
+ manageAppPermission, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
// Add the Settings app name since we masquerade it.
CharSequence appName = getSettingsAppName();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index b96efef49..812e4fe7c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -127,6 +127,9 @@ public class GrantPermissionsActivity extends SettingsActivity
@Override
public void onCreate(Bundle icicle) {
+ if (DeviceUtils.isAuto(this)) {
+ setTheme(R.style.GrantPermissions_Car_FilterTouches);
+ }
super.onCreate(icicle);
if (icicle == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index bf48e28ae..f8594ac62 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -256,10 +256,10 @@ public final class ManagePermissionsActivity extends SettingsActivity {
if (DeviceUtils.isAuto(this)) {
if (allPermissions) {
androidXFragment = AutoAllAppPermissionsFragment.newInstance(packageName,
- userHandle);
+ userHandle, sessionId);
} else {
androidXFragment = AutoAppPermissionsFragment.newInstance(packageName,
- userHandle);
+ userHandle, sessionId, /* isSystemPermsScreen= */ true);
}
} else if (DeviceUtils.isWear(this)) {
androidXFragment = AppPermissionsFragmentWear.newInstance(packageName);
@@ -268,7 +268,7 @@ public final class ManagePermissionsActivity extends SettingsActivity {
.AppPermissionsFragment.newInstance(packageName, userHandle);
} else {
Bundle args = AppPermissionGroupsFragment.createArgs(packageName, userHandle,
- sessionId, true);
+ sessionId, /* isSystemPermsScreen= */ true);
setNavGraph(args, R.id.app_permission_groups);
return;
}
@@ -296,7 +296,8 @@ public final class ManagePermissionsActivity extends SettingsActivity {
return;
}
if (DeviceUtils.isAuto(this)) {
- androidXFragment = AutoPermissionAppsFragment.newInstance(permissionName);
+ androidXFragment =
+ AutoPermissionAppsFragment.newInstance(permissionName, sessionId);
} else if (DeviceUtils.isTelevision(this)) {
androidXFragment = com.android.permissioncontroller.permission.ui.television
.PermissionAppsFragment.newInstance(permissionName);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
index c1d26ddce..73aeb76e2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
@@ -26,6 +26,7 @@ import android.os.Bundle
import android.os.UserHandle
import android.util.Log
import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.preference.Preference
@@ -52,11 +53,9 @@ import java.text.Collator
* A fragment displaying all applications that are unused as well as the option to remove them
* and to open them.
*/
-class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
+class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>,
- UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
-
- private val INFO_MSG_CATEGORY = "info_msg_category"
+ UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
private lateinit var viewModel: UnusedAppsViewModel
private lateinit var collator: Collator
@@ -64,6 +63,7 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
private var isFirstLoad = false
companion object {
+ public const val INFO_MSG_CATEGORY = "info_msg_category"
private const val SHOW_LOAD_DELAY_MS = 200L
private const val INFO_MSG_KEY = "info_msg"
private const val ELEVATION_HIGH = 8f
@@ -91,10 +91,6 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
}
}
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- // empty
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val preferenceFragment: PF = requirePreferenceFragment()
@@ -153,14 +149,15 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
*/
private fun createPreferenceScreen() {
val preferenceFragment: PF = requirePreferenceFragment()
- val preferenceScreen = preferenceManager.inflateFromResource(
+ val preferenceScreen = preferenceFragment.preferenceManager.inflateFromResource(
context,
R.xml.unused_app_categories,
/* rootPreferences= */ null)
preferenceFragment.preferenceScreen = preferenceScreen
val infoMsgCategory = preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)
- val footerPreference = preferenceFragment.createFooterPreference(context!!)
+ val footerPreference = preferenceFragment.createFooterPreference(
+ preferenceFragment.preferenceManager.context)
footerPreference.key = INFO_MSG_KEY
infoMsgCategory?.addPreference(footerPreference)
}
@@ -211,7 +208,8 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
var pref = category.findPreference<UnusedAppPref>(key)
if (pref == null) {
pref = removedPrefs[key] ?: preferenceFragment.createUnusedAppPref(
- activity!!.application, pkgName, user, preferenceManager.context!!)
+ activity!!.application, pkgName, user,
+ preferenceFragment.preferenceManager.context)
pref.key = key
pref.title = KotlinUtils.getPackageLabel(activity!!.application, pkgName, user)
}
@@ -253,9 +251,7 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
}
}
- val infoMsgCategory =
- preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
- infoMsgCategory.isVisible = !allCategoriesEmpty
+ preferenceFragment.setEmptyState(allCategoriesEmpty)
if (isFirstLoad) {
if (categorizedPackages[Months.SIX]!!.isNotEmpty() ||
@@ -377,5 +373,12 @@ class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
user: UserHandle,
context: Context
): UnusedAppPref
+
+ /**
+ * Updates the state based on whether the content is empty.
+ *
+ * @param empty whether the content is empty
+ */
+ fun setEmptyState(empty: Boolean)
}
} \ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
index f065f1c09..e509d8a5b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
@@ -16,6 +16,8 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -62,18 +64,19 @@ public class AutoAllAppPermissionsFragment extends AutoSettingsFrameFragment {
/** Creates an {@link AutoAllAppPermissionsFragment} with no filter. */
public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull UserHandle userHandle) {
- return newInstance(packageName, /* filterGroup= */ null, userHandle);
+ @NonNull UserHandle userHandle, long sessionId) {
+ return newInstance(packageName, /* filterGroup= */ null, userHandle, sessionId);
}
/** Creates an {@link AutoAllAppPermissionsFragment} with a specific filter group. */
public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull String filterGroup, @NonNull UserHandle userHandle) {
+ @NonNull String filterGroup, @NonNull UserHandle userHandle, long sessionId) {
AutoAllAppPermissionsFragment instance = new AutoAllAppPermissionsFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, filterGroup);
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
instance.setArguments(arguments);
return instance;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
index 3181db4a3..22b9c41b1 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
@@ -16,31 +16,35 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
+import android.text.BidiFormatter;
import android.util.Log;
import android.view.View;
import android.widget.RadioButton;
-import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.TypedArrayUtils;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
@@ -50,35 +54,26 @@ import androidx.preference.TwoStatePreference;
import com.android.car.ui.AlertDialogBuilder;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.Permission;
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
-import com.android.permissioncontroller.permission.utils.LocationUtils;
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel;
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
-import com.android.permissioncontroller.permission.utils.SafetyNetLogger;
-import com.android.permissioncontroller.permission.utils.Utils;
import com.android.settingslib.RestrictedLockUtils;
-import java.lang.annotation.Retention;
-import java.util.List;
+import java.util.Map;
+
+import kotlin.Pair;
/** Settings related to a particular permission for the given app. */
-public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
+public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
+ implements AppPermissionViewModel.ConfirmDialogShowingFragment {
private static final String LOG_TAG = "AppPermissionFragment";
-
- @Retention(SOURCE)
- @IntDef(value = {CHANGE_FOREGROUND, CHANGE_BACKGROUND}, flag = true)
- @interface ChangeTarget {
- }
-
- static final int CHANGE_FOREGROUND = 1;
- static final int CHANGE_BACKGROUND = 2;
- static final int CHANGE_BOTH = CHANGE_FOREGROUND | CHANGE_BACKGROUND;
+ private static final long POST_DELAY_MS = 20;
@NonNull
- private AppPermissionGroup mGroup;
-
+ private TwoStatePreference mAllowPermissionPreference;
@NonNull
private TwoStatePreference mAlwaysPermissionPreference;
@NonNull
@@ -88,14 +83,19 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
@NonNull
private AutoTwoTargetPreference mDetailsPreference;
- private boolean mHasConfirmedRevoke;
-
- /**
- * Listens for changes to the permission of the app the permission is currently getting
- * granted to. {@code null} when unregistered.
- */
- @Nullable
- private PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
+ @NonNull
+ private AppPermissionViewModel mViewModel;
+ @NonNull
+ private String mPackageName;
+ @NonNull
+ private String mPermGroupName;
+ @NonNull
+ private UserHandle mUser;
+ @NonNull
+ private String mPackageLabel;
+ @NonNull
+ private String mPermGroupLabel;
+ private Drawable mPackageIcon;
/**
* Listens for changes to the app the permission is currently getting granted to. {@code null}
@@ -114,7 +114,8 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
*/
@NonNull
public static AutoAppPermissionFragment newInstance(@NonNull String packageName,
- @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle) {
+ @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle,
+ @NonNull long sessionId) {
AutoAppPermissionFragment fragment = new AutoAppPermissionFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
@@ -124,75 +125,34 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName);
}
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
fragment.setArguments(arguments);
return fragment;
}
@Override
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext()));
+ }
+
+ @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- mHasConfirmedRevoke = false;
-
- mGroup = getAppPermissionGroup();
- if (mGroup == null) {
- requireActivity().setResult(Activity.RESULT_CANCELED);
- requireActivity().finish();
- return;
+ mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
+ if (mPermGroupName == null) {
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
}
-
+ mUser = getArguments().getParcelable(Intent.EXTRA_USER);
+ mPackageLabel = BidiFormatter.getInstance().unicodeWrap(
+ KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName,
+ mUser));
+ mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(),
+ mPermGroupName).toString();
+ mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(),
+ mPackageName, mUser);
setHeaderLabel(
- requireContext().getString(R.string.app_permission_title, mGroup.getFullLabel()));
- }
-
- private void setResult(@GrantPermissionsViewHandler.Result int result) {
- Intent intent = new Intent()
- .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED,
- requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result);
- requireActivity().setResult(Activity.RESULT_OK, intent);
- }
-
- private AppPermissionGroup getAppPermissionGroup() {
- Activity activity = requireActivity();
- Context context = getPreferenceManager().getContext();
-
- String packageName = requireArguments().getString(Intent.EXTRA_PACKAGE_NAME);
- String groupName = requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
- if (groupName == null) {
- groupName = requireArguments().getString(Intent.EXTRA_PERMISSION_NAME);
- }
- PackageItemInfo groupInfo = Utils.getGroupInfo(groupName, context);
- List<PermissionInfo> groupPermInfos = Utils.getGroupPermissionInfos(groupName, context);
- if (groupInfo == null || groupPermInfos == null) {
- Log.i(LOG_TAG, "Illegal group: " + groupName);
- return null;
- }
- UserHandle userHandle = requireArguments().getParcelable(Intent.EXTRA_USER);
- if (userHandle == null) {
- Log.e(LOG_TAG, "User handle is null");
- return null;
- }
- PackageInfo packageInfo = AutoPermissionsUtils.getPackageInfo(activity, packageName,
- userHandle);
- if (packageInfo == null) {
- Log.i(LOG_TAG, "PackageInfo is null");
- return null;
- }
- AppPermissionGroup group = AppPermissionGroup.create(context, packageInfo, groupInfo,
- groupPermInfos, false);
-
- if (group == null || !Utils.shouldShowPermission(context, group)) {
- Log.i(LOG_TAG, "Illegal group: " + (group == null ? "null" : group.getName()));
- return null;
- }
-
- return group;
- }
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext()));
+ requireContext().getString(R.string.app_permission_title, mPermGroupLabel));
}
@Override
@@ -202,14 +162,18 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
PreferenceScreen screen = getPreferenceScreen();
screen.addPreference(
AutoPermissionsUtils.createHeaderPreference(requireContext(),
- mGroup.getApp().applicationInfo));
+ mPackageIcon, mPackageName, mPackageLabel));
// Add permissions selector preferences.
PreferenceGroup permissionSelector = new PreferenceCategory(requireContext());
permissionSelector.setTitle(
- getString(R.string.app_permission_header, mGroup.getFullLabel()));
+ getString(R.string.app_permission_header, mPermGroupLabel));
screen.addPreference(permissionSelector);
+ mAllowPermissionPreference = new SelectedPermissionPreference(requireContext());
+ mAllowPermissionPreference.setTitle(R.string.app_permission_button_allow);
+ permissionSelector.addPreference(mAllowPermissionPreference);
+
mAlwaysPermissionPreference = new SelectedPermissionPreference(requireContext());
mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
permissionSelector.addPreference(mAlwaysPermissionPreference);
@@ -223,6 +187,35 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
mDenyPermissionPreference.setTitle(R.string.app_permission_button_deny);
permissionSelector.addPreference(mDenyPermissionPreference);
+ mAllowPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW);
+ setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_FOREGROUND,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW);
+ return true;
+ });
+ mAlwaysPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS);
+ setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS);
+ return true;
+ });
+ mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND);
+ setResult(GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_FOREGROUND_ONLY,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND);
+ return true;
+ });
+ mDenyPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.DENY);
+ setResult(GrantPermissionsViewHandler.DENIED);
+ requestChange(AppPermissionViewModel.ChangeRequest.REVOKE_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY);
+ return true;
+ });
+
mDetailsPreference = new AutoTwoTargetPreference(requireContext());
screen.addPreference(mDetailsPreference);
}
@@ -232,17 +225,11 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
super.onStart();
Activity activity = requireActivity();
- mPermissionChangeListener = new PermissionChangeListener(
- mGroup.getApp().applicationInfo.uid);
- PackageManager pm = activity.getPackageManager();
- pm.addOnPermissionsChangeListener(mPermissionChangeListener);
-
// Get notified when the package is removed.
- String packageName = mGroup.getApp().packageName;
- mPackageRemovalMonitor = new PackageRemovalMonitor(requireContext(), packageName) {
+ mPackageRemovalMonitor = new PackageRemovalMonitor(requireContext(), mPackageName) {
@Override
public void onPackageRemoved() {
- Log.w(LOG_TAG, packageName + " was uninstalled");
+ Log.w(LOG_TAG, mPackageName + " was uninstalled");
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
@@ -251,18 +238,22 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
// Check if the package was removed while this activity was not started.
try {
- activity.createPackageContextAsUser(packageName, /* flags= */ 0,
- mGroup.getUser()).getPackageManager().getPackageInfo(packageName,
+ activity.createPackageContextAsUser(mPackageName, /* flags= */ 0,
+ mUser).getPackageManager().getPackageInfo(mPackageName,
/* flags= */ 0);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(LOG_TAG, packageName + " was uninstalled while this activity was stopped", e);
+ Log.w(LOG_TAG, mPackageName + " was uninstalled while this activity was stopped", e);
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
- // Re-create the permission group in case permissions have changed and update the UI.
- mGroup = getAppPermissionGroup();
- updateUi();
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory(
+ getActivity().getApplication(), mPackageName, mPermGroupName, mUser, sessionId);
+ mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class);
+ mViewModel.getButtonStateLiveData().observe(this, this::setRadioButtonsState);
+ mViewModel.getDetailResIdLiveData().observe(this, this::setDetail);
+ mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail);
}
@Override
@@ -273,494 +264,115 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
mPackageRemovalMonitor.unregister();
mPackageRemovalMonitor = null;
}
-
- if (mPermissionChangeListener != null) {
- requireActivity().getPackageManager().removeOnPermissionsChangeListener(
- mPermissionChangeListener);
- mPermissionChangeListener = null;
- }
- }
-
- private void updateUi() {
- mDetailsPreference.setOnSecondTargetClickListener(null);
- mDetailsPreference.setVisible(false);
-
- if (mGroup.areRuntimePermissionsGranted()) {
- if (!mGroup.hasPermissionWithBackgroundMode()
- || (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted())) {
- setSelectedPermissionState(mAlwaysPermissionPreference);
- } else {
- setSelectedPermissionState(mForegroundOnlyPermissionPreference);
- }
- } else {
- setSelectedPermissionState(mDenyPermissionPreference);
- }
-
- mAlwaysPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
- return requestChange(/* requestGrant= */true, CHANGE_BOTH);
- });
- mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY);
- requestChange(/* requestGrant= */false, CHANGE_BACKGROUND);
- requestChange(/* requestGrant= */true, CHANGE_FOREGROUND);
- return true;
- });
- mDenyPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.DENIED);
- return requestChange(/* requestGrant= */ false, CHANGE_BOTH);
- });
-
- // Set the allow and foreground-only button states appropriately.
- if (mGroup.hasPermissionWithBackgroundMode()) {
- if (mGroup.getBackgroundPermissions() == null) {
- mAlwaysPermissionPreference.setVisible(false);
- } else {
- mForegroundOnlyPermissionPreference.setVisible(true);
- mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
- }
- } else {
- mForegroundOnlyPermissionPreference.setVisible(false);
- mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow);
- }
-
- // Handle the UI for various special cases.
- if (isSystemFixed() || isPolicyFullyFixed() || isForegroundDisabledByPolicy()) {
- // Disable changing permissions and potentially show administrator message.
- mAlwaysPermissionPreference.setEnabled(false);
- mForegroundOnlyPermissionPreference.setEnabled(false);
- mDenyPermissionPreference.setEnabled(false);
-
- RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
- if (admin != null) {
- mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget);
- mDetailsPreference.setOnSecondTargetClickListener(
- preference -> RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
- getContext(), admin));
- }
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else if (Utils.areGroupPermissionsIndividuallyControlled(requireContext(),
- mGroup.getName())) {
- // If the permissions are individually controlled, also show a link to the page that
- // lets you control them.
- mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget);
- mDetailsPreference.setOnSecondTargetClickListener(
- preference -> showAllPermissions(mGroup.getName()));
-
- updateDetailForIndividuallyControlledPermissionGroup();
- } else {
- if (mGroup.hasPermissionWithBackgroundMode()) {
- if (mGroup.getBackgroundPermissions() == null) {
- // The group has background permissions but the app did not request any. I.e.
- // The app can only switch between 'never" and "only in foreground".
- mAlwaysPermissionPreference.setEnabled(false);
-
- mDenyPermissionPreference.setOnPreferenceClickListener(v -> requestChange(false,
- CHANGE_FOREGROUND));
- } else {
- if (isBackgroundPolicyFixed()) {
- // If background policy is fixed, we only allow switching the foreground.
- // Note that this assumes that the background policy is fixed to deny,
- // since if it is fixed to grant, so is the foreground.
- mAlwaysPermissionPreference.setEnabled(false);
- setSelectedPermissionState(mForegroundOnlyPermissionPreference);
-
- mDenyPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(false, CHANGE_FOREGROUND));
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else if (isForegroundPolicyFixed()) {
- // Foreground permissions are fixed to allow (the first case above handles
- // fixing to deny), so we only allow toggling background permissions.
- mDenyPermissionPreference.setEnabled(false);
-
- mAlwaysPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(true, CHANGE_BACKGROUND));
- mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(false, CHANGE_BACKGROUND));
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else {
- // The default tri-state case is handled by default.
- }
- }
-
- } else {
- // The default bi-state case is handled by default.
- }
- }
}
- /**
- * Set the given permission state as the only checked permission state.
- */
- private void setSelectedPermissionState(@NonNull TwoStatePreference permissionState) {
- permissionState.setChecked(true);
- if (permissionState != mAlwaysPermissionPreference) {
- mAlwaysPermissionPreference.setChecked(false);
- }
- if (permissionState != mForegroundOnlyPermissionPreference) {
- mForegroundOnlyPermissionPreference.setChecked(false);
- }
- if (permissionState != mDenyPermissionPreference) {
- mDenyPermissionPreference.setChecked(false);
- }
- }
+ @Override
+ public void showConfirmDialog(AppPermissionViewModel.ChangeRequest changeRequest, int messageId,
+ int buttonPressed, boolean oneTime) {
+ Bundle args = new Bundle();
- /**
- * Are any permissions of this group fixed by the system, i.e. not changeable by the user.
- *
- * @return {@code true} iff any permission is fixed
- */
- private boolean isSystemFixed() {
- return mGroup.isSystemFixed();
- }
+ args.putInt(DefaultDenyDialog.MSG, messageId);
+ args.putSerializable(DefaultDenyDialog.CHANGE_REQUEST, changeRequest);
+ args.putSerializable(DefaultDenyDialog.BUTTON, buttonPressed);
- /**
- * Is any foreground permissions of this group fixed by the policy, i.e. not changeable by the
- * user.
- *
- * @return {@code true} iff any foreground permission is fixed
- */
- private boolean isForegroundPolicyFixed() {
- return mGroup.isPolicyFixed();
+ DefaultDenyDialog defaultDenyDialog = new DefaultDenyDialog();
+ defaultDenyDialog.setArguments(args);
+ defaultDenyDialog.setTargetFragment(this, 0);
+ defaultDenyDialog.show(requireFragmentManager().beginTransaction(),
+ DefaultDenyDialog.class.getName());
}
- /**
- * Is any background permissions of this group fixed by the policy, i.e. not changeable by the
- * user.
- *
- * @return {@code true} iff any background permission is fixed
- */
- private boolean isBackgroundPolicyFixed() {
- return mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().isPolicyFixed();
+ private void setResult(@GrantPermissionsViewHandler.Result int result) {
+ Intent intent = new Intent()
+ .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED,
+ requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME))
+ .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result);
+ requireActivity().setResult(Activity.RESULT_OK, intent);
}
- /**
- * Are there permissions fixed, so that the user cannot change the preference at all?
- *
- * @return {@code true} iff the permissions of this group are fixed
- */
- private boolean isPolicyFullyFixed() {
- return isForegroundPolicyFixed() && (mGroup.getBackgroundPermissions() == null
- || isBackgroundPolicyFixed());
+ private void setRadioButtonsState(
+ Map<AppPermissionViewModel.ButtonType, AppPermissionViewModel.ButtonState> states) {
+ setButtonState(mAllowPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW));
+ setButtonState(mAlwaysPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS));
+ setButtonState(mForegroundOnlyPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND));
+ setButtonState(mDenyPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.DENY));
}
- /**
- * Is the foreground part of this group disabled. If the foreground is disabled, there is no
- * need to possible grant background access.
- *
- * @return {@code true} iff the permissions of this group are fixed
- */
- private boolean isForegroundDisabledByPolicy() {
- return isForegroundPolicyFixed() && !mGroup.areRuntimePermissionsGranted();
+ private void setButtonState(TwoStatePreference button,
+ AppPermissionViewModel.ButtonState state) {
+ button.setVisible(state.isShown());
+ if (state.isShown()) {
+ button.setChecked(state.isChecked());
+ button.setEnabled(state.isEnabled());
+ }
}
/**
- * Get the app that acts as admin for this profile.
- *
- * @return The admin or {@code null} if there is no admin.
+ * Helper method to handle the UX edge case where the confirmation dialog is shown and two
+ * buttons are selected at once. This happens since the Auto UI doesn't use a proper radio
+ * group, so there is nothing that enforces that tapping on a button unchecks a previously
+ * checked button. Apart from this case, this UI is not necessary since the UI is entirely
+ * driven by the ViewModel.
*/
- @Nullable
- private RestrictedLockUtils.EnforcedAdmin getAdmin() {
- return RestrictedLockUtils.getProfileOrDeviceOwner(getContext(), mGroup.getUser());
+ private void checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType buttonType) {
+ mAllowPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW);
+ mAlwaysPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW_ALWAYS);
+ mForegroundOnlyPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND);
+ mDenyPermissionPreference.setChecked(buttonType == AppPermissionViewModel.ButtonType.DENY);
}
- /**
- * Update the detail in the case the permission group has individually controlled permissions.
- */
- private void updateDetailForIndividuallyControlledPermissionGroup() {
- int revokedCount = 0;
- List<Permission> permissions = mGroup.getPermissions();
- for (Permission permission : permissions) {
- if (!permission.isGrantedIncludingAppOp()) {
- revokedCount++;
- }
- }
-
- int resId;
- if (revokedCount == 0) {
- resId = R.string.permission_revoked_none;
- } else if (revokedCount == permissions.size()) {
- resId = R.string.permission_revoked_all;
- } else {
- resId = R.string.permission_revoked_count;
+ private void setDetail(Pair<Integer, Integer> detailResIds) {
+ if (detailResIds == null) {
+ mDetailsPreference.setVisible(false);
+ return;
}
-
- mDetailsPreference.setSummary(getString(resId, revokedCount));
- mDetailsPreference.setVisible(true);
- }
-
- /**
- * Update the detail of a permission group that is at least partially fixed by policy.
- */
- private void updateDetailForFixedByPolicyPermissionGroup() {
- RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
- AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
-
- boolean hasAdmin = admin != null;
-
- if (isSystemFixed()) {
- // Permission is fully controlled by the system and cannot be switched
-
- setDetail(R.string.permission_summary_enabled_system_fixed);
- } else if (isForegroundDisabledByPolicy()) {
- // Permission is fully controlled by policy and cannot be switched
-
- if (hasAdmin) {
- setDetail(R.string.disabled_by_admin);
- } else {
- // Disabled state will be displayed by switch, so no need to add text for that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else if (isPolicyFullyFixed()) {
- // Permission is fully controlled by policy and cannot be switched
-
- if (backgroundGroup == null) {
- if (hasAdmin) {
- setDetail(R.string.enabled_by_admin);
- } else {
- // Enabled state will be displayed by switch, so no need to add text for
- // that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else {
- if (backgroundGroup.areRuntimePermissionsGranted()) {
- if (hasAdmin) {
- setDetail(R.string.enabled_by_admin);
- } else {
- // Enabled state will be displayed by switch, so no need to add text for
- // that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else {
- if (hasAdmin) {
- setDetail(
- R.string.permission_summary_enabled_by_admin_foreground_only);
- } else {
- setDetail(
- R.string.permission_summary_enabled_by_policy_foreground_only);
- }
- }
- }
+ if (detailResIds.getSecond() != null) {
+ mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget);
+ mDetailsPreference.setOnSecondTargetClickListener(
+ v -> showAllPermissions(mPermGroupName));
+ mDetailsPreference.setSummary(
+ getString(detailResIds.getFirst(), detailResIds.getSecond()));
} else {
- // Part of the permission group can still be switched
-
- if (isBackgroundPolicyFixed()) {
- if (backgroundGroup.areRuntimePermissionsGranted()) {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_enabled_by_admin_background_only);
- } else {
- setDetail(R.string.permission_summary_enabled_by_policy_background_only);
- }
- } else {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_disabled_by_admin_background_only);
- } else {
- setDetail(R.string.permission_summary_disabled_by_policy_background_only);
- }
- }
- } else if (isForegroundPolicyFixed()) {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_enabled_by_admin_foreground_only);
- } else {
- setDetail(R.string.permission_summary_enabled_by_policy_foreground_only);
- }
- }
+ mDetailsPreference.setSummary(detailResIds.getFirst());
}
}
/**
- * Show the given string as informative text below permission picker preferences.
- *
- * @param strId the resourceId of the string to display.
- */
- private void setDetail(int strId) {
- mDetailsPreference.setSummary(strId);
- mDetailsPreference.setVisible(true);
- }
-
- /**
* Show all individual permissions in this group in a new fragment.
*/
private void showAllPermissions(@NonNull String filterGroup) {
- Fragment frag = AutoAllAppPermissionsFragment.newInstance(mGroup.getApp().packageName,
- filterGroup, UserHandle.getUserHandleForUid(mGroup.getApp().applicationInfo.uid));
+ Fragment frag = AutoAllAppPermissionsFragment.newInstance(mPackageName,
+ filterGroup, mUser,
+ getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
requireFragmentManager().beginTransaction()
.replace(android.R.id.content, frag)
.addToBackStack("AllPerms")
.commit();
}
- /**
- * Request to grant/revoke permissions group.
- *
- * <p>Does <u>not</u> handle:
- * <ul>
- * <li>Individually granted permissions</li>
- * <li>Permission groups with background permissions</li>
- * </ul>
- * <p><u>Does</u> handle:
- * <ul>
- * <li>Default grant permissions</li>
- * </ul>
- *
- * @param requestGrant If this group should be granted
- * @param changeTarget Which permission group (foreground/background/both) should be changed
- * @return If the request was processed.
- */
- private boolean requestChange(boolean requestGrant, @ChangeTarget int changeTarget) {
- if (LocationUtils.isLocationGroupAndProvider(getContext(), mGroup.getName(),
- mGroup.getApp().packageName)) {
- LocationUtils.showLocationDialog(getContext(),
- Utils.getAppLabel(mGroup.getApp().applicationInfo, requireContext()));
-
- // The request was denied, so update the buttons.
- updateUi();
- return false;
- }
-
- if (requestGrant) {
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- if (!mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
-
- mGroup.grantRuntimePermissions(true, false);
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- if (!mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
-
- mGroup.getBackgroundPermissions().grantRuntimePermissions(true, false);
- }
- }
- } else {
- boolean showDefaultDenyDialog = false;
-
- if ((changeTarget & CHANGE_FOREGROUND) != 0
- && mGroup.areRuntimePermissionsGranted()) {
- showDefaultDenyDialog = mGroup.hasGrantedByDefaultPermission()
- || !mGroup.doesSupportRuntimePermissions()
- || mGroup.hasInstallToRuntimeSplit();
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- AppPermissionGroup bgPerm = mGroup.getBackgroundPermissions();
- showDefaultDenyDialog |= bgPerm.hasGrantedByDefaultPermission()
- || !bgPerm.doesSupportRuntimePermissions()
- || bgPerm.hasInstallToRuntimeSplit();
- }
- }
-
- if (showDefaultDenyDialog && !mHasConfirmedRevoke) {
- showDefaultDenyDialog(changeTarget);
- updateUi();
- return false;
- } else {
- if ((changeTarget & CHANGE_FOREGROUND) != 0
- && mGroup.areRuntimePermissionsGranted()) {
- if (mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
-
- mGroup.revokeRuntimePermissions(false);
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
-
- mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
- }
- }
- }
- }
-
- updateUi();
-
- return true;
- }
-
- /**
- * Show a dialog that warns the user that she/he is about to revoke permissions that were
- * granted by default.
- *
- * <p>The order of operation to revoke a permission granted by default is:
- * <ol>
- * <li>{@code showDefaultDenyDialog}</li>
- * <li>{@link DefaultDenyDialog#onCreateDialog}</li>
- * <li>{@link AutoAppPermissionFragment#onDenyAnyWay}</li>
- * </ol>
- *
- * @param changeTarget Whether background or foreground should be changed
- */
- private void showDefaultDenyDialog(@ChangeTarget int changeTarget) {
- Bundle args = new Bundle();
-
- boolean showGrantedByDefaultWarning = false;
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- showGrantedByDefaultWarning = mGroup.hasGrantedByDefaultPermission();
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- showGrantedByDefaultWarning |=
- mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
- }
+ private void setAdminSupportDetail(RestrictedLockUtils.EnforcedAdmin admin) {
+ if (admin != null) {
+ mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget);
+ mDetailsPreference.setOnSecondTargetClickListener(v ->
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin)
+ );
}
-
- args.putInt(DefaultDenyDialog.MSG, showGrantedByDefaultWarning ? R.string.system_warning
- : R.string.old_sdk_deny_warning);
- args.putInt(DefaultDenyDialog.CHANGE_TARGET, changeTarget);
-
- DefaultDenyDialog defaultDenyDialog = new DefaultDenyDialog();
- defaultDenyDialog.setArguments(args);
- defaultDenyDialog.setTargetFragment(this, 0);
- defaultDenyDialog.show(requireFragmentManager().beginTransaction(),
- DefaultDenyDialog.class.getName());
}
/**
- * Once we user has confirmed that he/she wants to revoke a permission that was granted by
- * default, actually revoke the permissions.
- *
- * @param changeTarget whether to change foreground, background, or both.
- * @see #showDefaultDenyDialog(int)
+ * Request to grant/revoke permissions group.
*/
- private void onDenyAnyWay(@ChangeTarget int changeTarget) {
- boolean hasDefaultPermissions = false;
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- if (mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
-
- mGroup.revokeRuntimePermissions(false);
- hasDefaultPermissions = mGroup.hasGrantedByDefaultPermission();
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
-
- mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
- hasDefaultPermissions |=
- mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
- }
- }
-
- if (hasDefaultPermissions || !mGroup.doesSupportRuntimePermissions()) {
- mHasConfirmedRevoke = true;
- }
- updateUi();
+ private void requestChange(AppPermissionViewModel.ChangeRequest changeRequest,
+ int buttonClicked) {
+ mViewModel.requestChange(/* setOneTime= */false, /* fragment= */ this,
+ /* defaultDeny= */this, changeRequest, buttonClicked);
}
/** Preference used to represent apps that can be picked as a default app. */
@@ -787,12 +399,14 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
* A dialog warning the user that they are about to deny a permission that was granted by
* default.
*
- * @see #showDefaultDenyDialog(int)
+ * @see #showConfirmDialog(AppPermissionViewModel.ChangeRequest, int, int, boolean)
*/
public static class DefaultDenyDialog extends DialogFragment {
private static final String MSG = DefaultDenyDialog.class.getName() + ".arg.msg";
- private static final String CHANGE_TARGET = DefaultDenyDialog.class.getName()
- + ".arg.changeTarget";
+ private static final String CHANGE_REQUEST = DefaultDenyDialog.class.getName()
+ + ".arg.changeRequest";
+ private static final String BUTTON = DefaultDenyDialog.class.getName()
+ + ".arg.button";
@NonNull
@Override
@@ -801,31 +415,21 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
return new AlertDialogBuilder(getContext())
.setMessage(requireArguments().getInt(MSG))
.setNegativeButton(R.string.cancel,
- (dialog, which) -> fragment.updateUi())
+ (dialog, which) -> dialog.cancel())
.setPositiveButton(R.string.grant_dialog_button_deny_anyway,
(dialog, which) ->
- fragment.onDenyAnyWay(requireArguments().getInt(CHANGE_TARGET)))
+ fragment.mViewModel.onDenyAnyWay(
+ (AppPermissionViewModel.ChangeRequest)
+ getArguments().getSerializable(CHANGE_REQUEST),
+ getArguments().getInt(BUTTON),
+ false))
.create();
}
- }
-
- /**
- * A listener for permission changes.
- */
- private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
- private final int mUid;
-
- PermissionChangeListener(int uid) {
- mUid = uid;
- }
@Override
- public void onPermissionsChanged(int uid) {
- if (uid == mUid) {
- Log.w(LOG_TAG, "Permissions changed.");
- mGroup = getAppPermissionGroup();
- updateUi();
- }
+ public void onCancel(DialogInterface dialog) {
+ AutoAppPermissionFragment fragment = (AutoAppPermissionFragment) getTargetFragment();
+ fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue());
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
index fed489e13..659ed3d65 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
@@ -17,52 +17,69 @@
package com.android.permissioncontroller.permission.ui.auto;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+import static java.util.concurrent.TimeUnit.DAYS;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
-import androidx.preference.PreferenceScreen;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissions;
+import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.ui.Category;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory;
-import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import java.text.Collator;
+import java.time.Instant;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
/** Screen to show the permissions for a specific application. */
-public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
+public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment implements
+ PermissionUsages.PermissionsUsagesChangeCallback {
private static final String LOG_TAG = AutoAppPermissionsFragment.class.getSimpleName();
- private static final String KEY_ALLOWED_PERMISSIONS_GROUP = "allowed_permissions_group";
- private static final String KEY_DENIED_PERMISSIONS_GROUP = "denied_permissions_group";
+ private static final String IS_SYSTEM_PERMS_SCREEN = "_is_system_screen";
+ private static final String KEY_ALLOWED_PERMISSIONS_GROUP = Category.ALLOWED.getCategoryName();
+ private static final String KEY_DENIED_PERMISSIONS_GROUP = Category.DENIED.getCategoryName();
private AppPermissionGroupsViewModel mViewModel;
- private AppPermissions mAppPermissions;
- private PreferenceScreen mExtraScreen;
private String mPackageName;
+ private boolean mIsFirstLoad;
+ private UserHandle mUser;
+ private PermissionUsages mPermissionUsages;
+ private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+ private boolean mIsSystemPermsScreen;
private Collator mCollator;
@@ -70,16 +87,19 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
* @return A new fragment
*/
public static AutoAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull UserHandle userHandle) {
+ @NonNull UserHandle userHandle, long sessionId, boolean isSystemPermsScreen) {
return setPackageNameAndUserHandle(new AutoAppPermissionsFragment(), packageName,
- userHandle);
+ userHandle, sessionId, isSystemPermsScreen);
}
private static <T extends Fragment> T setPackageNameAndUserHandle(@NonNull T fragment,
- @NonNull String packageName, @NonNull UserHandle userHandle) {
+ @NonNull String packageName, @NonNull UserHandle userHandle, long sessionId,
+ boolean isSystemPermsScreen) {
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
+ arguments.putBoolean(IS_SYSTEM_PERMS_SCREEN, isSystemPermsScreen);
fragment.setArguments(arguments);
return fragment;
}
@@ -90,6 +110,8 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
setLoading(true);
mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ mUser = getArguments().getParcelable(Intent.EXTRA_USER);
+ mIsSystemPermsScreen = getArguments().getBoolean(IS_SYSTEM_PERMS_SCREEN, true);
UserHandle userHandle = getArguments().getParcelable(Intent.EXTRA_USER);
Activity activity = requireActivity();
PackageInfo packageInfo = AutoPermissionsUtils.getPackageInfo(activity, mPackageName,
@@ -101,8 +123,6 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
return;
}
- mAppPermissions = new AppPermissions(activity, packageInfo, /* sortGroups= */ true,
- () -> getActivity().finish());
mCollator = Collator.getInstance(
getContext().getResources().getConfiguration().getLocales().get(0));
AppPermissionGroupsViewModelFactory factory =
@@ -111,13 +131,27 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
mViewModel = new ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel.class);
setHeaderLabel(getContext().getString(R.string.app_permissions));
- setAction(getContext().getString(R.string.all_permissions), v -> showAllPermissions());
+ if (mIsSystemPermsScreen) {
+ setAction(getContext().getString(R.string.all_permissions), v -> showAllPermissions());
+ }
createPreferenceCategories(packageInfo);
mViewModel.getPackagePermGroupsLiveData().observe(this, this::updatePreferences);
if (mViewModel.getPackagePermGroupsLiveData().getValue() != null) {
updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue());
}
+
+ if (SdkLevel.isAtLeastS()) {
+ mPermissionUsages = new PermissionUsages(getContext());
+
+ long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
+ - DAYS.toMillis(
+ AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS),
+ Instant.EPOCH.toEpochMilli());
+ mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+ false, false, this, false);
+ }
}
@Override
@@ -125,10 +159,27 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
+
+ @Override
+ @RequiresApi(Build.VERSION_CODES.S)
+ public void onPermissionUsagesChanged() {
+ if (mPermissionUsages.getUsages().isEmpty()) {
+ return;
+ }
+ if (getContext() == null) {
+ // Async result has come in after our context is gone.
+ return;
+ }
+
+ mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
+ updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue());
+ }
+
private void showAllPermissions() {
Fragment frag = AutoAllAppPermissionsFragment.newInstance(
getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
- getArguments().getParcelable(Intent.EXTRA_USER));
+ getArguments().getParcelable(Intent.EXTRA_USER),
+ getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
getFragmentManager().beginTransaction()
.replace(android.R.id.content, frag)
.addToBackStack("AllPerms")
@@ -155,10 +206,8 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
bindUi(packageInfo);
}
- // TODO(b/179383241): Make full use of groupMap data in this method
private void updatePreferences(
Map<Category, List<AppPermissionGroupsViewModel.GroupUiInfo>> groupMap) {
- mAppPermissions.refresh();
Context context = getPreferenceManager().getContext();
if (context == null) {
return;
@@ -173,108 +222,73 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
return;
}
- PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
- PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+ Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
+ mViewModel.extractGroupUsageLastAccessTime(groupUsageLastAccessTime, mAppPermissionUsages,
+ mPackageName);
- allowed.removeAll();
- denied.removeAll();
+ for (Category grantCategory : groupMap.keySet()) {
+ if (Category.ASK.equals(grantCategory)) {
+ // skip ask category for auto
+ continue;
+ }
+ PreferenceCategory category = getPreferenceScreen().findPreference(
+ grantCategory.getCategoryName());
+ if (grantCategory.equals(Category.ALLOWED_FOREGROUND)) {
+ category = findPreference(Category.ALLOWED.getCategoryName());
+ }
+ int numExtraPerms = 0;
- if (mExtraScreen != null) {
- mExtraScreen.removeAll();
- mExtraScreen.addPreference(AutoPermissionsUtils.createHeaderPreference(getContext(),
- mAppPermissions.getPackageInfo().applicationInfo));
- }
+ category.removeAll();
- Preference extraPerms = new Preference(context);
- extraPerms.setIcon(R.drawable.ic_toc);
- extraPerms.setTitle(R.string.additional_permissions);
- boolean extraPermsAreAllowed = false;
- ArrayList<AppPermissionGroup> groups = new ArrayList<>(
- mAppPermissions.getPermissionGroups());
- groups.sort((x, y) -> mCollator.compare(x.getLabel(), y.getLabel()));
- allowed.setOrderingAsAdded(true);
- denied.setOrderingAsAdded(true);
+ for (AppPermissionGroupsViewModel.GroupUiInfo groupInfo : groupMap.get(grantCategory)) {
+ if (groupInfo.isSystem() == mIsSystemPermsScreen) {
+ Preference preference = createPermissionPreference(getContext(), groupInfo,
+ groupUsageLastAccessTime);
+ category.addPreference(preference);
+ } else if (!groupInfo.isSystem()) {
+ numExtraPerms++;
+ }
+ }
- for (int i = 0; i < groups.size(); i++) {
- AppPermissionGroup group = groups.get(i);
- if (!Utils.shouldShowPermission(getContext(), group)) {
- continue;
+
+ if (numExtraPerms > 0) {
+ setAdditionalPermissionsPreference(category, numExtraPerms, context);
}
- boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
-
- Preference preference = createPermissionPreference(getContext(), group);
- if (isPlatform) {
- PreferenceCategory category =
- group.areRuntimePermissionsGranted() ? allowed : denied;
- category.addPreference(preference);
- } else {
- if (mExtraScreen == null) {
- mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
- mExtraScreen.addPreference(
- AutoPermissionsUtils.createHeaderPreference(getContext(),
- mAppPermissions.getPackageInfo().applicationInfo));
- }
- mExtraScreen.addPreference(preference);
- if (group.areRuntimePermissionsGranted()) {
- extraPermsAreAllowed = true;
- }
+ if (category.getPreferenceCount() == 0) {
+ setNoPermissionPreference(category, grantCategory, context);
}
- }
- if (mExtraScreen != null) {
- extraPerms.setOnPreferenceClickListener(preference -> {
- AutoAppPermissionsFragment.AdditionalPermissionsFragment
- frag = new AutoAppPermissionsFragment.AdditionalPermissionsFragment();
- setPackageNameAndUserHandle(frag,
- getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
- getArguments().getParcelable(Intent.EXTRA_USER));
- frag.setTargetFragment(AutoAppPermissionsFragment.this, 0);
- getFragmentManager().beginTransaction()
- .replace(android.R.id.content, frag)
- .addToBackStack(null)
- .commit();
- return true;
- });
- // Delete 1 to account for app header preference.
- int count = mExtraScreen.getPreferenceCount() - 1;
- extraPerms.setSummary(getResources().getQuantityString(
- R.plurals.additional_permissions_more, count,
- count));
- PreferenceCategory category = extraPermsAreAllowed ? allowed : denied;
- category.addPreference(extraPerms);
+ KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreferences, false);
}
- if (allowed.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(getString(R.string.no_permissions_allowed));
- empty.setSelectable(false);
- allowed.addPreference(empty);
- }
- if (denied.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(getString(R.string.no_permissions_denied));
- empty.setSelectable(false);
- denied.addPreference(empty);
- }
+ if (mIsFirstLoad) {
+ logAppPermissionsFragmentView();
+ mIsFirstLoad = false;
+ }
setLoading(false);
}
- private Preference createPermissionPreference(Context context, AppPermissionGroup group) {
+ private Preference createPermissionPreference(Context context,
+ AppPermissionGroupsViewModel.GroupUiInfo groupInfo,
+ Map<String, Long> groupUsageLastAccessTime) {
+ String groupName = groupInfo.getGroupName();
Preference preference = new Preference(context);
- Drawable icon = Utils.loadDrawable(context.getPackageManager(),
- group.getIconPkg(), group.getIconResId());
- preference.setKey(group.getName());
- preference.setTitle(group.getFullLabel());
- preference.setIcon(Utils.applyTint(context, icon, android.R.attr.colorControlNormal));
- preference.setSummary(getPreferenceSummary(group));
+ preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName));
+ preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName));
+ preference.setKey(groupName);
+ String summary = mViewModel.getPreferenceSummary(groupInfo, context,
+ groupUsageLastAccessTime.get(groupName));
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
+ }
preference.setOnPreferenceClickListener(pref -> {
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, group.getApp().packageName);
- intent.putExtra(Intent.EXTRA_PERMISSION_NAME, group.getPermissions().get(0).getName());
- intent.putExtra(Intent.EXTRA_USER, group.getUser());
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
+ intent.putExtra(Intent.EXTRA_PERMISSION_NAME, groupName);
+ intent.putExtra(Intent.EXTRA_USER, mUser);
intent.putExtra(EXTRA_CALLER_NAME, AutoAppPermissionsFragment.class.getName());
context.startActivity(intent);
return true;
@@ -282,39 +296,107 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
return preference;
}
- private String getPreferenceSummary(AppPermissionGroup group) {
- return getGroupSummary(group);
+ private void setAdditionalPermissionsPreference(PreferenceCategory category, int numExtraPerms,
+ Context context) {
+ Preference extraPerms = new Preference(context);
+ extraPerms.setIcon(R.drawable.ic_toc);
+ extraPerms.setTitle(R.string.additional_permissions);
+ extraPerms.setOnPreferenceClickListener(preference -> {
+ AutoAppPermissionsFragment
+ frag = AutoAppPermissionsFragment.newInstance(mPackageName, mUser,
+ getArguments().getLong(EXTRA_SESSION_ID), false);
+ frag.setTargetFragment(AutoAppPermissionsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack(null)
+ .commit();
+ return true;
+ });
+ extraPerms.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, numExtraPerms,
+ numExtraPerms));
+ category.addPreference(extraPerms);
}
- private String getGroupSummary(AppPermissionGroup group) {
- if (group.hasPermissionWithBackgroundMode() && group.areRuntimePermissionsGranted()) {
- AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
- if (backgroundGroup == null || !backgroundGroup.areRuntimePermissionsGranted()) {
- return getContext().getString(R.string.permission_subtitle_only_in_foreground);
- }
+ private void setNoPermissionPreference(PreferenceCategory category, Category grantCategory,
+ Context context) {
+ Preference empty = new Preference(context);
+ empty.setKey(getString(grantCategory.equals(Category.DENIED)
+ ? R.string.no_permissions_denied : R.string.no_permissions_allowed));
+ empty.setTitle(empty.getKey());
+ empty.setSelectable(false);
+ category.addPreference(empty);
+ }
+
+ private int comparePreferences(Preference lhs, Preference rhs) {
+ String additionalTitle = lhs.getContext().getString(R.string.additional_permissions);
+ if (lhs.getTitle().equals(additionalTitle)) {
+ return 1;
+ } else if (rhs.getTitle().equals(additionalTitle)) {
+ return -1;
}
- return null;
+ return mCollator.compare(lhs.getTitle().toString(),
+ rhs.getTitle().toString());
}
- /**
- * Class that shows additional permissions.
- */
- public static class AdditionalPermissionsFragment extends AutoSettingsFrameFragment {
- AutoAppPermissionsFragment mOuterFragment;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // Set this before calling super.onCreate as it is needed in onCreatePreferences
- // (which is called from super.onCreate).
- mOuterFragment = (AutoAppPermissionsFragment) getTargetFragment();
- super.onCreate(savedInstanceState);
- setHeaderLabel(mOuterFragment.getHeaderLabel());
+ private void logAppPermissionsFragmentView() {
+ Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
}
+ String permissionSubtitleOnlyInForeground =
+ context.getString(R.string.permission_subtitle_only_in_foreground);
+
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(mOuterFragment.mExtraScreen);
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ long viewId = new Random().nextLong();
+
+ PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
+
+ int numAllowed = allowed.getPreferenceCount();
+ for (int i = 0; i < numAllowed; i++) {
+ Preference preference = allowed.getPreference(i);
+ if (preference.getTitle().equals(getString(R.string.no_permissions_allowed))) {
+ // R.string.no_permission_allowed was added to PreferenceCategory
+ continue;
+ }
+
+ int category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+ if (preference.getSummary() != null
+ && permissionSubtitleOnlyInForeground.contentEquals(preference.getSummary())) {
+ category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+ }
+
+ logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(), category);
+ }
+
+ PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+
+ int numDenied = denied.getPreferenceCount();
+ for (int i = 0; i < numDenied; i++) {
+ Preference preference = denied.getPreference(i);
+ if (preference.getTitle().equals(getString(R.string.no_permissions_denied))) {
+ // R.string.no_permission_denied was added to PreferenceCategory
+ continue;
+ }
+ logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(),
+ APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED);
+ }
+ }
+
+ private void logAppPermissionsFragmentViewEntry(
+ long sessionId, long viewId, String permissionGroupName, int category) {
+ Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(),
+ mPackageName, mUser);
+ if (uid == null) {
+ return;
}
+ PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId,
+ permissionGroupName, uid, mPackageName, category);
+ Log.v(LOG_TAG, "AutoAppPermissionFragment view logged with sessionId=" + sessionId
+ + " viewId=" + viewId + " permissionGroupName=" + permissionGroupName + " uid="
+ + uid + " packageName="
+ + mPackageName + " category=" + category);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
deleted file mode 100644
index 0de51c813..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.ui.auto;
-
-import com.android.permissioncontroller.R;
-
-/** Shows additional non-system permissions that can be granted/denied. */
-public class AutoManageCustomPermissionsFragment extends AutoManagePermissionsFragment {
-
- @Override
- protected int getScreenHeaderRes() {
- return R.string.additional_permissions;
- }
-
- @Override
- protected void updatePermissionsUi() {
- updatePermissionsUi(/* addSystemPermissions= */ false);
- }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
new file mode 100644
index 000000000..e6033993a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.auto;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
+
+import java.util.List;
+
+/**
+ * Shows additional non-system permissions that can be granted/denied.
+ *
+ * Largely based on
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment}.
+ */
+public class AutoManageOtherPermissionsFragment extends AutoSettingsFrameFragment {
+ private static final String KEY_UNUSED_CATEGORY = "category_unused";
+ private static final String KEY_ADDITIONAL_CATEGORY = "category_additional";
+
+ private PreferenceCategory mUnusedCategory;
+ private PreferenceCategory mAdditionalCategory;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setLoading(true);
+ setHeaderLabel(getString(R.string.other_permissions_label));
+
+ final Application application = getActivity().getApplication();
+ final ViewModelProvider.Factory factory =
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ final ManagePermissionsViewModel viewModel = new ViewModelProvider(this, factory)
+ .get(ManagePermissionsViewModel.class);
+
+ viewModel.getUnusedPermissionGroups().observe(this, this::onUnusedPermissionGroupsChanged);
+ viewModel.getAdditionalPermissionGroups().observe(this,
+ this::onAdditionalPermissionGroupsChanged);
+ viewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+ this::onHasUnusedOrAdditionalPermissionGroups);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final Context context = getPreferenceManager().getContext();
+ final PreferenceScreen screen = getPreferenceScreen();
+ // We add categories here, but make them invisible until the data is loaded.
+ mUnusedCategory = new PreferenceCategory(context);
+ mUnusedCategory.setKey(KEY_UNUSED_CATEGORY);
+ mUnusedCategory.setTitle(R.string.not_used_permissions_label);
+ mUnusedCategory.setSummary(R.string.not_used_permissions_description);
+ mUnusedCategory.setVisible(false);
+ screen.addPreference(mUnusedCategory);
+
+ mAdditionalCategory = new PreferenceCategory(context);
+ mAdditionalCategory.setKey(KEY_ADDITIONAL_CATEGORY);
+ mAdditionalCategory.setTitle(R.string.additional_permissions_label);
+ mAdditionalCategory.setSummary(R.string.additional_permissions_description);
+ mAdditionalCategory.setVisible(false);
+ screen.addPreference(mAdditionalCategory);
+ }
+
+ private void onUnusedPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+ updateCategory(mUnusedCategory, permissionGroups);
+ }
+
+ private void onAdditionalPermissionGroupsChanged(
+ List<PermGroupPackagesUiInfo> permissionGroups) {
+ updateCategory(mAdditionalCategory, permissionGroups);
+ }
+
+ private void onHasUnusedOrAdditionalPermissionGroups(Boolean hasPGs) {
+ if (!hasPGs) {
+ // There are not more permissions on this screen - go back.
+ getParentFragmentManager().popBackStack();
+ }
+ }
+
+ private void updateCategory(PreferenceCategory category,
+ List<PermGroupPackagesUiInfo> permissionGroups) {
+ final Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
+ }
+
+ PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, category,
+ permissionGroups);
+ // Only show the category if it's not empty.
+ category.setVisible(!permissionGroups.isEmpty());
+
+ setLoading(false);
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
deleted file mode 100644
index 49ea5eb15..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.ui.auto;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.preference.Preference;
-
-import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
-import com.android.permissioncontroller.permission.utils.Utils;
-
-import java.text.Collator;
-import java.util.ArrayList;
-
-/** Base class to show the list of permissions that can be granted/denied. */
-abstract class AutoManagePermissionsFragment extends AutoSettingsFrameFragment implements
- PermissionGroups.PermissionsGroupsChangeCallback, Preference.OnPreferenceClickListener {
-
- private static final String LOG_TAG = "ManagePermissionsFragment";
-
- static final String OS_PKG = "android";
-
- private PermissionGroups mPermissions;
-
- private Collator mCollator;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLoading(true);
-
- mPermissions = new PermissionGroups(getContext(), requireActivity().getLoaderManager(),
- /* callback= */ this, /* getAppUiInfo= */ false,
- /* getNonPlatformPermissions= */ true);
- mCollator = Collator.getInstance(
- getContext().getResources().getConfiguration().getLocales().get(0));
-
- setHeaderLabel(getString(getScreenHeaderRes()));
- }
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- String key = preference.getKey();
-
- PermissionGroup group = mPermissions.getGroup(key);
- if (group == null) {
- return false;
- }
-
- Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- .putExtra(Intent.EXTRA_PERMISSION_NAME, key);
- try {
- getActivity().startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOG_TAG, "No app to handle " + intent);
- }
-
- return true;
- }
-
- /** Returns the header string resource. */
- @StringRes
- protected abstract int getScreenHeaderRes();
-
- /** Returns the current permissions. */
- protected PermissionGroups getPermissions() {
- return mPermissions;
- }
-
- @Override
- public void onPermissionGroupsChanged() {
- updatePermissionsUi();
- }
-
- /** Update the preferences to show the new {@link #getPermissions() permissions}. */
- protected abstract void updatePermissionsUi();
-
- /**
- * Add preferences for all permissions of a type to the preference screen.
- */
- protected void updatePermissionsUi(boolean addSystemPermissions) {
- Context context = getPreferenceManager().getContext();
- if (context == null || getActivity() == null) {
- return;
- }
-
- ArrayList<PermissionGroup> groups = new ArrayList<>(mPermissions.getGroups());
- groups.sort((x, y) -> mCollator.compare(x.getLabel(), y.getLabel()));
- getPreferenceScreen().removeAll();
- getPreferenceScreen().setOrderingAsAdded(true);
-
- // Use this to speed up getting the info for all of the PermissionApps below.
- // Create a new one for each refresh to make sure it has fresh data.
- for (int i = 0; i < groups.size(); i++) {
- PermissionGroup group = groups.get(i);
- boolean isSystemPermission = group.getDeclaringPackage().equals(OS_PKG);
-
- if (addSystemPermissions == isSystemPermission) {
- Preference preference = findPreference(group.getName());
-
- if (preference == null) {
- preference = new Preference(context);
- preference.setOnPreferenceClickListener(this);
- preference.setKey(group.getName());
- preference.setIcon(Utils.applyTint(context, group.getIcon(),
- android.R.attr.colorControlNormal));
- preference.setTitle(group.getLabel());
- // Set blank summary so that no resizing/jumping happens when the summary is
- // loaded.
- preference.setSummary(" ");
- preference.setPersistent(false);
- getPreferenceScreen().addPreference(preference);
- }
- preference.setSummary(
- getString(R.string.app_permissions_group_summary, group.getGranted(),
- group.getTotal()));
- }
- }
- if (getPreferenceScreen().getPreferenceCount() != 0) {
- setLoading(false);
- }
- }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
index c3d9ed2a0..8bbcde6ed 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
@@ -16,31 +16,43 @@
package com.android.permissioncontroller.permission.ui.auto;
+
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
import android.app.Application;
+import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
-/** Shows the standard permissions that can be granted/denied. */
-public class AutoManageStandardPermissionsFragment extends AutoManagePermissionsFragment {
+/**
+ * Shows the standard permissions that can be granted/denied.
+ *
+ * Largely based on the implementation of
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment}.
+ */
+public class AutoManageStandardPermissionsFragment extends AutoSettingsFrameFragment {
- private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
- private static final String AUTO_REVOKE_KEY = "auto_revoke_key";
- private ManageStandardPermissionsViewModel mViewModel;
+ private static final String KEY_OTHER_PERMISSIONS = "other_permissions";
+ private static final String KEY_AUTO_REVOKE = "auto_revoke_key";
+ private ManagePermissionsViewModel mManagePermissionsViewModel;
+ private ManageStandardPermissionsViewModel mManageStandardPermissionsViewModel;
/** Returns a new instance of {@link AutoManageStandardPermissionsFragment}. */
public static AutoManageStandardPermissionsFragment newInstance() {
@@ -50,84 +62,108 @@ public class AutoManageStandardPermissionsFragment extends AutoManagePermissions
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setLoading(true);
+ setHeaderLabel(getString(R.string.app_permission_manager));
final Application application = getActivity().getApplication();
- mViewModel = new ViewModelProvider(this,
+ final ViewModelProvider.Factory viewModelFactory =
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ mManagePermissionsViewModel = new ViewModelProvider(this, viewModelFactory)
+ .get(ManagePermissionsViewModel.class);
+ mManageStandardPermissionsViewModel = new ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application))
.get(ManageStandardPermissionsViewModel.class);
-
- mViewModel.getUiDataLiveData().observe(this, permissionGroups -> {
- if (permissionGroups != null) {
- updatePermissionsUi();
- } else {
- getActivity().finish();
- }
- });
-
- mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi());
+ mManagePermissionsViewModel.getUsedPermissionGroups().observe(this,
+ this::onPermissionGroupsChanged);
+ mManageStandardPermissionsViewModel.getNumAutoRevoked().observe(this,
+ this::onNumAutoRevokedChanged);
}
@Override
- protected int getScreenHeaderRes() {
- return R.string.app_permission_manager;
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
- @Override
- protected void updatePermissionsUi() {
- updatePermissionsUi(/* addSystemPermissions= */ true);
-
- // Check if we need an additional permissions preference
- List<PermissionGroup> groups = getPermissions().getGroups();
- int numExtraPermissions = 0;
- for (PermissionGroup group : groups) {
- if (!group.getDeclaringPackage().equals(AutoManagePermissionsFragment.OS_PKG)) {
- numExtraPermissions++;
- }
+ private void onPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+ final Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
}
- Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
- EXTRA_PREFS_KEY);
- if (numExtraPermissions == 0) {
- if (additionalPermissionsPreference != null) {
- getPreferenceScreen().removePreference(additionalPermissionsPreference);
- }
+ final PreferenceScreen screen = getPreferenceScreen();
+
+ // First check if "Other preferences" button exists. If it does: remove it, but hold on to
+ // the reference - we'll it add back later, after the preferences for the permission groups
+ // have been updated. If it does not exist: create and hold on to it.
+ Preference otherPermissionsPreference = screen.findPreference(KEY_OTHER_PERMISSIONS);
+ if (otherPermissionsPreference == null) {
+ otherPermissionsPreference = buildOtherPermissionsPreference(context);
} else {
- if (additionalPermissionsPreference == null) {
- additionalPermissionsPreference = new Preference(
- getPreferenceManager().getContext());
- additionalPermissionsPreference.setKey(EXTRA_PREFS_KEY);
- additionalPermissionsPreference.setIcon(Utils.applyTint(getActivity(),
- R.drawable.ic_more_items,
- android.R.attr.colorControlNormal));
- additionalPermissionsPreference.setTitle(R.string.additional_permissions);
- additionalPermissionsPreference.setOnPreferenceClickListener(preference -> {
- AutoManageCustomPermissionsFragment frag =
- new AutoManageCustomPermissionsFragment();
- frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
- /* requestCode= */ 0);
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.replace(android.R.id.content, frag);
- ft.addToBackStack(null);
- ft.commit();
- return true;
- });
+ screen.removePreference(otherPermissionsPreference);
+ // The PreferenceScreen is ordering items as added
+ // (see PreferenceGroup#setOrderingAsAdded()), which means that it assigns positional
+ // indexes ("order") to Preferences incrementally as they are added, BUT ONLY IF their
+ // current "order" is the DEFAULT_ORDER.
+ // However, when the Preferences are removed from the group they keep their "order" and
+ // thus when they are re-added to a group (same or another) their "order" does not get
+ // re-assigned, so they may show up at the position they previously were at.
+ // We want the otherPermissionsPreference to always be the last in the list, so reset
+ // its "order" to DEFAULT, so that we add last to the group, it indeed goes into the
+ // last position.
+ otherPermissionsPreference.setOrder(Preference.DEFAULT_ORDER);
+ }
- getPreferenceScreen().addPreference(additionalPermissionsPreference);
- }
+ PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, screen,
+ permissionGroups);
- additionalPermissionsPreference.setSummary(getResources().getQuantityString(
- R.plurals.additional_permissions_more, numExtraPermissions,
- numExtraPermissions));
- }
+ screen.addPreference(otherPermissionsPreference);
- Integer numAutoRevoked = mViewModel.getNumAutoRevoked().getValue();
+ // load initial auto-revoke count, if it is ready
+ Integer numAutoRevoked = mManageStandardPermissionsViewModel.getNumAutoRevoked().getValue();
+ onNumAutoRevokedChanged(numAutoRevoked);
- Preference autoRevokePreference = getPreferenceScreen().findPreference(AUTO_REVOKE_KEY);
+ setLoading(false);
+ }
+
+
+ private Preference buildOtherPermissionsPreference(Context context) {
+ final Preference preference = new Preference(context);
+ preference.setPersistent(false);
+ preference.setKey(KEY_OTHER_PERMISSIONS);
+ preference.setTitle(R.string.other_permissions_label);
+ preference.setIcon(
+ Utils.applyTint(
+ context, R.drawable.ic_more_items, android.R.attr.colorControlNormal));
+ preference.setOnPreferenceClickListener(p -> {
+ AutoManageOtherPermissionsFragment frag =
+ new AutoManageOtherPermissionsFragment();
+ frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
+ /* requestCode= */ 0);
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.replace(android.R.id.content, frag);
+ ft.addToBackStack(null);
+ ft.commit();
+ return true;
+ });
+ // Make invisible for now and subscribe to the LiveData that tracks whether there are any
+ // unused or additional permissions.
+ preference.setVisible(false);
+ mManagePermissionsViewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+ preference::setVisible);
+ return preference;
+ }
+
+ private void onNumAutoRevokedChanged(Integer numAutoRevoked) {
+ // to prevent ui jank, don't display auto-revoke until categories have loaded
+ if (mManagePermissionsViewModel.getUsedPermissionGroups().getValue() == null) {
+ return;
+ }
+ Preference autoRevokePreference = getPreferenceScreen().findPreference(KEY_AUTO_REVOKE);
if (numAutoRevoked != null && numAutoRevoked != 0) {
if (autoRevokePreference == null) {
autoRevokePreference = new Preference(getPreferenceManager().getContext());
autoRevokePreference.setOrder(-1);
- autoRevokePreference.setKey(AUTO_REVOKE_KEY);
+ autoRevokePreference.setKey(KEY_AUTO_REVOKE);
autoRevokePreference.setSingleLineTitle(false);
autoRevokePreference.setIcon(R.drawable.ic_info_outline);
autoRevokePreference.setTitle(
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
index dfd536192..94fd6b9eb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
@@ -16,289 +16,317 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.permission.ui.Category.ASK;
+import static com.android.permissioncontroller.permission.ui.Category.DENIED;
+import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
import android.util.ArrayMap;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps.Callback;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionControlPreference;
+import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
+import com.android.permissioncontroller.permission.ui.Category;
+import com.android.permissioncontroller.permission.ui.handheld.SmartIconLoadPackagePermissionPreference;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.settingslib.utils.applications.AppUtils;
import java.text.Collator;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
+import java.util.Random;
-/** Shows the list of applications which have (or do not have) the given permission. */
-public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements Callback {
+import kotlin.Pair;
- private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
- private static final String KEY_ALLOWED_PERMISSIONS_GROUP = "allowed_permissions_group";
- private static final String KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP =
- "allowed_foreground_permissions_group";
- private static final String KEY_DENIED_PERMISSIONS_GROUP = "denied_permissions_group";
+/** Shows the list of applications which have (or do not have) the given permission. */
+public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements
+ PermissionUsages.PermissionsUsagesChangeCallback {
- private static final String SHOW_SYSTEM_KEY = AutoPermissionAppsFragment.class.getName()
- + KEY_SHOW_SYSTEM_PREFS;
+ private static final String LOG_TAG = "AutoPermissionAppsFragment";
+ private static final String KEY_EMPTY = "_empty";
/** Creates a new instance of {@link AutoPermissionAppsFragment} for the given permission. */
- public static AutoPermissionAppsFragment newInstance(String permissionName) {
- return setPermissionName(new AutoPermissionAppsFragment(), permissionName);
+ public static AutoPermissionAppsFragment newInstance(String permissionName, long sessionId) {
+ return setPermissionName(new AutoPermissionAppsFragment(), permissionName, sessionId);
}
- private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
+ private static <T extends Fragment> T setPermissionName(
+ T fragment, String permissionName, long sessionId) {
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
fragment.setArguments(arguments);
return fragment;
}
- private PermissionApps mPermissionApps;
-
- private boolean mShowSystem;
- private boolean mHasSystemApps;
+ private PermissionAppsViewModel mViewModel;
+ private PermissionUsages mPermissionUsages;
+ private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+ private String mPermGroupName;
private Collator mCollator;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setLoading(true);
- if (savedInstanceState != null) {
- mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY);
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
+ if (mPermGroupName == null) {
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
}
- setLoading(true);
+ Drawable icon = KotlinUtils.INSTANCE.getPermGroupIcon(getContext(), mPermGroupName);
+ CharSequence label = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), mPermGroupName);
+ CharSequence description = KotlinUtils.INSTANCE.getPermGroupDescription(getContext(),
+ mPermGroupName);
- String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
- mPermissionApps = new PermissionApps(getActivity(), groupName, /* callback= */ this);
- mPermissionApps.refresh(/* getUiInfo= */ true);
+ setHeaderLabel(label);
+ Preference header = new Preference(getContext());
+ header.setTitle(label);
+ header.setIcon(icon);
+ header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), mPermGroupName,
+ description));
+ getPreferenceScreen().addPreference(header);
mCollator = Collator.getInstance(
getContext().getResources().getConfiguration().getLocales().get(0));
- setShowSystemAppsToggle();
- bindUi(mPermissionApps, groupName);
- }
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mPermissionApps.refresh(/* getUiInfo= */ true);
+ PermissionAppsViewModelFactory factory =
+ new PermissionAppsViewModelFactory(getActivity().getApplication(), mPermGroupName,
+ this, new Bundle());
+ mViewModel = new ViewModelProvider(this, factory).get(PermissionAppsViewModel.class);
+
+ mViewModel.getCategorizedAppsLiveData().observe(this, this::onPackagesLoaded);
+ mViewModel.getShouldShowSystemLiveData().observe(this, this::updateMenu);
+ mViewModel.getHasSystemAppsLiveData().observe(this, this::hideSystemAppToggleIfNecessary);
+
+ // If the build type is below S, the app ops for permission usage can't be found. Thus, we
+ // shouldn't load permission usages, for them.
+ if (SdkLevel.isAtLeastS()) {
+ Context context = getPreferenceManager().getContext();
+ mPermissionUsages = new PermissionUsages(context);
+
+ long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
+ mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+ false, false, this, false);
+ }
}
- private void setShowSystemAppsToggle() {
- if (!mHasSystemApps) {
- setAction(/* label= */ null, /* onClickListener= */ null);
- return;
+ private void updateMenu(Boolean showSystem) {
+ if (showSystem == null) {
+ showSystem = false;
}
-
// Show the opposite label from the current state.
String label;
- if (mShowSystem) {
+ if (showSystem) {
label = getString(R.string.menu_hide_system);
} else {
label = getString(R.string.menu_show_system);
}
- setAction(label, v -> {
- mShowSystem = !mShowSystem;
- if (mPermissionApps.getApps() != null) {
- onPermissionsLoaded(mPermissionApps);
- }
- setShowSystemAppsToggle();
- });
- }
-
- private void bindUi(PermissionApps permissionApps, @NonNull String groupName) {
- CharSequence label = permissionApps.getFullLabel();
- setHeaderLabel(label);
-
- Drawable icon = permissionApps.getIcon();
- Preference header = new Preference(getContext());
- header.setTitle(label);
- header.setIcon(icon);
- header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), groupName,
- permissionApps.getDescription()));
- getPreferenceScreen().addPreference(header);
-
- PreferenceGroup allowed = new PreferenceCategory(getContext());
- allowed.setKey(KEY_ALLOWED_PERMISSIONS_GROUP);
- allowed.setTitle(R.string.allowed_header);
- allowed.setVisible(false);
- getPreferenceScreen().addPreference(allowed);
-
- PreferenceGroup foreground = new PreferenceCategory(getContext());
- foreground.setKey(KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
- foreground.setTitle(R.string.allowed_foreground_header);
- foreground.setVisible(false);
- getPreferenceScreen().addPreference(foreground);
-
- PreferenceGroup denied = new PreferenceCategory(getContext());
- denied.setKey(KEY_DENIED_PERMISSIONS_GROUP);
- denied.setTitle(R.string.denied_header);
- denied.setVisible(false);
- getPreferenceScreen().addPreference(denied);
+ boolean showSystemFinal = showSystem;
+ setAction(label, v -> mViewModel.updateShowSystem(!showSystemFinal));
}
- @Override
- public void onPermissionsLoaded(PermissionApps permissionApps) {
+ /**
+ * Main differences between this phone implementation and this one are:
+ * <ul>
+ * <li>No special handling for scoped storage</li>
+ * </ul>
+ */
+ private void onPackagesLoaded(Map<Category, List<Pair<String, UserHandle>>> categories) {
+ Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
+ ALLOWED_FOREGROUND.getCategoryName());
+ if (additionalPermissionsPreference == null) {
+ // This preference resources includes the "Ask" permission group. That's okay for Auto
+ // even though Auto doesn't support the one-time permission because the code later in
+ // this method will hide unused permission groups.
+ addPreferencesFromResource(R.xml.allowed_denied);
+ }
+ // Hide allowed foreground label by default, to avoid briefly showing it before updating
+ findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
Context context = getPreferenceManager().getContext();
- if (context == null || getActivity() == null) {
+ if (context == null || getActivity() == null || categories == null) {
return;
}
- PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
- PreferenceCategory allowedForeground = findPreference(
- KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
- PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
-
- allowed.setOrderingAsAdded(true);
- allowedForeground.setOrderingAsAdded(true);
- denied.setOrderingAsAdded(true);
-
Map<String, Preference> existingPrefs = new ArrayMap<>();
- int numPreferences = allowed.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = allowed.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- allowed.removeAll();
- numPreferences = allowedForeground.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = allowedForeground.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- allowedForeground.removeAll();
- numPreferences = denied.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = denied.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- denied.removeAll();
- mHasSystemApps = false;
- boolean hasPermissionWithBackgroundMode = false;
-
- ArrayList<PermissionApps.PermissionApp> sortedApps = new ArrayList<>(
- permissionApps.getApps());
- sortedApps.sort((x, y) -> {
- int result = mCollator.compare(x.getLabel(), y.getLabel());
- if (result == 0) {
- result = x.getUid() - y.getUid();
+ // Start at 1 since the header preference will always be in the 0th index
+ for (int i = 1; i < getPreferenceScreen().getPreferenceCount(); i++) {
+ PreferenceCategory category = (PreferenceCategory)
+ getPreferenceScreen().getPreference(i);
+ category.setOrderingAsAdded(true);
+ int numPreferences = category.getPreferenceCount();
+ for (int j = 0; j < numPreferences; j++) {
+ Preference preference = category.getPreference(j);
+ existingPrefs.put(preference.getKey(), preference);
}
- return result;
- });
-
- for (int i = 0; i < sortedApps.size(); i++) {
- PermissionApps.PermissionApp app = sortedApps.get(i);
- AppPermissionGroup group = app.getPermissionGroup();
+ category.removeAll();
+ }
- hasPermissionWithBackgroundMode =
- hasPermissionWithBackgroundMode || group.hasPermissionWithBackgroundMode();
+ long viewIdForLogging = new Random().nextLong();
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
- if (!Utils.shouldShowPermission(getContext(), group)) {
- continue;
- }
+ Boolean showAlways = mViewModel.getShowAllowAlwaysStringLiveData().getValue();
+ if (showAlways != null && showAlways) {
+ findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_always_header);
+ } else {
+ findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_header);
+ }
- if (!app.getAppInfo().enabled) {
+ // A mapping of user + packageName to their last access timestamps for the permission group.
+ Map<String, Long> groupUsageLastAccessTime =
+ mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
+
+ for (Category grantCategory : categories.keySet()) {
+ List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
+ PreferenceCategory category = findPreference(grantCategory.getCategoryName());
+
+ // If this category is empty set up the empty preference.
+ if (packages.size() == 0) {
+ Preference empty = new Preference(context);
+ empty.setSelectable(false);
+ empty.setKey(category.getKey() + KEY_EMPTY);
+ if (grantCategory.equals(ALLOWED)) {
+ empty.setTitle(getString(R.string.no_apps_allowed));
+ } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+ category.setVisible(false);
+ } else if (grantCategory.equals(ASK)) {
+ category.setVisible(false);
+ } else {
+ empty.setTitle(getString(R.string.no_apps_denied));
+ }
+ category.addPreference(empty);
continue;
+ } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+ category.setVisible(true);
+ } else if (grantCategory.equals(ASK)) {
+ category.setVisible(true);
}
- String key = app.getKey();
- Preference existingPref = existingPrefs.get(key);
- if (existingPref != null) {
- // Without this, existing preferences remember their old order.
- existingPref.setOrder(Preference.DEFAULT_ORDER);
- }
+ for (Pair<String, UserHandle> packageUserLabel : packages) {
+ String packageName = packageUserLabel.getFirst();
+ UserHandle user = packageUserLabel.getSecond();
- boolean isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(group);
+ String key = user + packageName;
- if (isSystemApp) {
- mHasSystemApps = true;
- }
+ Long lastAccessTime = groupUsageLastAccessTime.get(key);
+ Pair<String, Integer> summaryTimestamp = Utils
+ .getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime, context, mPermGroupName);
- if (isSystemApp && !mShowSystem) {
- continue;
- }
+ Preference existingPref = existingPrefs.get(key);
+ if (existingPref != null) {
+ updatePreferenceSummary(existingPref, summaryTimestamp);
+ category.addPreference(existingPref);
+ continue;
+ }
- PreferenceCategory category;
- if (group.areRuntimePermissionsGranted()) {
- if (!group.hasPermissionWithBackgroundMode()
- || (group.getBackgroundPermissions() != null
- && group.getBackgroundPermissions().areRuntimePermissionsGranted())) {
- category = allowed;
- } else {
- category = allowedForeground;
+ SmartIconLoadPackagePermissionPreference pref =
+ new SmartIconLoadPackagePermissionPreference(getActivity().getApplication(),
+ packageName, user, context);
+ pref.setKey(key);
+ pref.setTitle(KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(),
+ packageName, user));
+ pref.setOnPreferenceClickListener((Preference p) -> {
+ Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
+ intent.putExtra(Intent.EXTRA_USER, user);
+ intent.putExtra(EXTRA_CALLER_NAME, getClass().getName());
+ intent.putExtra(EXTRA_SESSION_ID, sessionId);
+ startActivity(intent);
+ return true;
+ });
+ pref.setTitleContentDescription(AppUtils.getAppContentDescription(context,
+ packageName, user.getIdentifier()));
+
+ updatePreferenceSummary(pref, summaryTimestamp);
+
+ category.addPreference(pref);
+ if (!mViewModel.getCreationLogged()) {
+ logPermissionAppsFragmentCreated(packageName, user, viewIdForLogging,
+ grantCategory.equals(ALLOWED), grantCategory.equals(ALLOWED_FOREGROUND),
+ grantCategory.equals(DENIED));
}
- } else {
- category = denied;
}
+ KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreference, false);
+ }
- if (existingPref != null) {
- category.addPreference(existingPref);
- continue;
- }
+ mViewModel.setCreationLogged(true);
+
+ setLoading(false);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
+ }
- PermissionControlPreference pref = new PermissionControlPreference(context, group,
- PermissionAppsFragment.class.getName());
- pref.setKey(key);
- pref.setIcon(app.getIcon());
- pref.setTitle(Utils.getFullAppLabel(app.getAppInfo(), context));
- pref.setEllipsizeEnd();
- pref.useSmallerIcon();
- category.addPreference(pref);
+ private void hideSystemAppToggleIfNecessary(Boolean hasSystemApps) {
+ if (hasSystemApps == null || !hasSystemApps) {
+ setAction(/* label= */ null, /* onClickListener= */ null);
}
+ }
- if (hasPermissionWithBackgroundMode) {
- allowed.setTitle(R.string.allowed_always_header);
+ private void updatePreferenceSummary(Preference preference,
+ Pair<String, Integer> summaryTimestamp) {
+ String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
}
+ }
- if (allowed.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(R.string.no_apps_allowed);
- empty.setSelectable(false);
- allowed.addPreference(empty);
+ @Override
+ @RequiresApi(Build.VERSION_CODES.S)
+ public void onPermissionUsagesChanged() {
+ if (mPermissionUsages.getUsages().isEmpty()) {
+ return;
+ }
+ if (getContext() == null) {
+ // Async result has come in after our context is gone.
+ return;
}
- allowed.setVisible(true);
- allowedForeground.setVisible(allowedForeground.getPreferenceCount() > 0);
+ mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
+ onPackagesLoaded(mViewModel.getCategorizedAppsLiveData().getValue());
+ }
- if (denied.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(R.string.no_apps_denied);
- empty.setSelectable(false);
- denied.addPreference(empty);
- }
- denied.setVisible(true);
+ private int comparePreference(Preference lhs, Preference rhs) {
+ return mViewModel.comparePreference(mCollator, lhs, rhs);
+ }
- setShowSystemAppsToggle();
- setLoading(false);
+ private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
+ boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
+ mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+ isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+ mPermGroupName, LOG_TAG);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
index 075869c6b..16544fc08 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
@@ -59,4 +59,14 @@ public final class AutoPermissionsUtils {
preference.setTitle(Utils.getFullAppLabel(appInfo, context));
return preference;
}
+
+ /** Creates a {@link Preference} which shows the app icon and app name. */
+ public static Preference createHeaderPreference(Context context, Drawable packageIcon,
+ String packageName, String packageLabel) {
+ Preference preference = new Preference(context);
+ preference.setIcon(packageIcon);
+ preference.setKey(packageName);
+ preference.setTitle(packageLabel);
+ return preference;
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
index 2a62d22f2..a7006683b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
@@ -20,10 +20,12 @@ import android.content.Context
import android.os.Bundle
import android.os.UserHandle
import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
import com.android.permissioncontroller.R
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
import com.android.car.ui.utils.ViewUtils
import com.android.car.ui.utils.ViewUtils.LazyLayoutView
@@ -34,6 +36,8 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
UnusedAppsFragment.Parent<AutoUnusedAppsPreference> {
companion object {
+ private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key"
+
/** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): AutoUnusedAppsFragment {
@@ -61,8 +65,8 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
// initially focus on focus parking view and then shift focus to recyclerview once it has
// loaded
- ViewUtils.hideFocus(getListView().getRootView())
- val lazyLayoutView = getListView() as LazyLayoutView
+ ViewUtils.hideFocus(getCarUiRecyclerView().getView().getRootView())
+ val lazyLayoutView = getCarUiRecyclerView() as LazyLayoutView
ViewUtils.initFocus(lazyLayoutView)
}
@@ -97,4 +101,25 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(),
override fun setTitle(title: CharSequence) {
headerLabel = title
}
+
+ override fun setEmptyState(empty: Boolean) {
+ val infoMsgCategory =
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ val noUnusedAppsPreference: Preference? =
+ infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
+ if (empty && noUnusedAppsPreference == null) {
+ infoMsgCategory.addPreference(createNoUnusedAppsPreference())
+ } else if (noUnusedAppsPreference != null) {
+ noUnusedAppsPreference.setVisible(empty)
+ }
+ }
+
+ private fun createNoUnusedAppsPreference(): Preference {
+ val preference = Preference(context)
+ preference.title = getString(R.string.zero_unused_apps)
+ preference.key = UNUSED_PREFERENCE_KEY
+ preference.isSelectable = false
+ preference.order = 0
+ return preference
+ }
} \ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 9023a34ce..a66b15fec 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -160,6 +160,7 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
}
CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setSecure(true);
item.setTitle(mContext.getString(stringId));
item.setOnItemClickedListener(i -> {
mDialog.setOnDismissListener(null);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index 999ca5911..bb0390f85 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -24,10 +24,6 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_
import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_CONTENT_PROVIDER;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_TODAY;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_YESTERDAY;
-import static com.android.permissioncontroller.permission.utils.Utils.NOT_IN_LAST_24H;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -68,9 +64,9 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState;
import com.android.permissioncontroller.permission.ui.Category;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory;
@@ -106,7 +102,6 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
private static final String ASSISTANT_MIC_CATEGORY_KEY = "_ASSISTANT_MIC_KEY";
private static final String ASSISTANT_MIC_SWITCH_KEY = "_ASSISTANT_MIC_SWITCH_KEY";
private static final String ASSISTANT_MIC_SUMMARY_KEY = "_ASSISTANT_MIC_SUMMARY_KEY";
- private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1;
static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
@@ -187,7 +182,8 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
mPermissionUsages = new PermissionUsages(context);
long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS),
+ - DAYS.toMillis(
+ AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS),
Instant.EPOCH.toEpochMilli());
mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
@@ -272,39 +268,6 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
}
}
- private void extractGroupUsageLastAccessTime(Map<String, Long> accessTime) {
- accessTime.clear();
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS), Instant.EPOCH.toEpochMilli());
-
- int numApps = mAppPermissionUsages.size();
- for (int appIndex = 0; appIndex < numApps; appIndex++) {
- AppPermissionUsage appUsage = mAppPermissionUsages.get(appIndex);
- if (!appUsage.getPackageName().equals(mPackageName)) {
- continue;
- }
-
- List<AppPermissionUsage.GroupUsage> appGroups = appUsage.getGroupUsages();
- int numGroups = appGroups.size();
- for (int groupIndex = 0; groupIndex < numGroups; groupIndex++) {
- AppPermissionUsage.GroupUsage groupUsage = appGroups.get(groupIndex);
- long lastAccessTime = groupUsage.getLastAccessTime();
- String groupName = groupUsage.getGroup().getName();
- if (lastAccessTime == 0 || lastAccessTime < filterTimeBeginMillis) {
- continue;
- }
-
- // We might have another AppPermissionUsage entry that's of the same packageName
- // but with a different uid. In that case, we want to grab the max lastAccessTime
- // as the last usage to show.
- lastAccessTime = Math.max(
- accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
- lastAccessTime);
- accessTime.put(groupName, lastAccessTime);
- }
- }
- }
-
private void updatePreferences(Map<Category, List<GroupUiInfo>> groupMap) {
if (groupMap == null) {
return;
@@ -328,7 +291,8 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
}
Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
- extractGroupUsageLastAccessTime(groupUsageLastAccessTime);
+ mViewModel.extractGroupUsageLastAccessTime(groupUsageLastAccessTime, mAppPermissionUsages,
+ mPackageName);
findPreference(Category.ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
@@ -367,100 +331,10 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName));
preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName));
preference.setKey(groupName);
- switch (groupInfo.getSubtitle()) {
- case BACKGROUND:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_background);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_background,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(getString(
- R.string.app_perms_24h_access_yest_background,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(
- R.string.permission_subtitle_background);
- }
-
- break;
- case MEDIA_ONLY:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_media_only);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_media_only,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_yest_media_only,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(R.string.permission_subtitle_media_only);
- }
-
- break;
- case ALL_FILES:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_all_files);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_all_files,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_yest_all_files,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(R.string.permission_subtitle_all_files);
- }
-
- break;
- case FOREGROUND_ONLY:
- // We don't want to note the foreground access
- default:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access_yest,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- }
+ String summary = mViewModel.getPreferenceSummary(groupInfo, context,
+ groupUsageLastAccessTime.get(groupName));
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
}
// Add an info icon if the package handles ACTION_VIEW_PERMISSION_USAGE.
PackageManager packageManager = requireActivity().getPackageManager();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
index 55686cf38..eaf90de87 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
@@ -22,9 +22,11 @@ import android.os.Bundle
import android.os.UserHandle
import android.view.MenuItem
import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
import com.android.permissioncontroller.R
import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
/**
* Handheld wrapper, with customizations, around [UnusedAppsFragment].
@@ -109,4 +111,10 @@ class HandheldUnusedAppsFragment : PermissionsFrameFragment(),
override fun setTitle(title: CharSequence) {
requireActivity().setTitle(title)
}
+
+ override fun setEmptyState(empty: Boolean) {
+ val infoMsgCategory =
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ infoMsgCategory.isVisible = !empty
+ }
} \ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
index 4491557d3..b28f4902f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
@@ -17,23 +17,12 @@ package com.android.permissioncontroller.permission.ui.handheld;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
import static com.android.permissioncontroller.permission.ui.Category.ASK;
import static com.android.permissioncontroller.permission.ui.Category.DENIED;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
import static com.android.permissioncontroller.permission.ui.handheld.dashboard.UtilsKt.shouldShowPermissionsDashboard;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_CONTENT_PROVIDER;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_TODAY;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_YESTERDAY;
-import static com.android.permissioncontroller.permission.utils.Utils.NOT_IN_LAST_24H;
-
-import static java.util.concurrent.TimeUnit.DAYS;
import android.Manifest;
import android.app.ActionBar;
@@ -46,7 +35,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -59,12 +47,11 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.ui.Category;
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
@@ -73,9 +60,7 @@ import com.android.settingslib.HelpUtils;
import com.android.settingslib.utils.applications.AppUtils;
import java.text.Collator;
-import java.time.Instant;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
@@ -98,7 +83,6 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full";
private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped";
private static final int SHOW_LOAD_DELAY_MS = 200;
- private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1;
private static final int MENU_PERMISSION_USAGE = MENU_HIDE_SYSTEM + 1;
@@ -167,9 +151,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
Context context = getPreferenceManager().getContext();
mPermissionUsages = new PermissionUsages(context);
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS),
- Instant.EPOCH.toEpochMilli());
+ long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
false, false, this, false);
@@ -309,8 +291,8 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
}
// A mapping of user + packageName to their last access timestamps for the permission group.
- Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
- extractGroupUsageLastAccessTime(groupUsageLastAccessTime);
+ Map<String, Long> groupUsageLastAccessTime =
+ mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
for (Category grantCategory : categories.keySet()) {
List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
@@ -422,57 +404,12 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private void updatePreferenceSummary(Preference preference,
Pair<String, Integer> summaryTimestamp) {
- @Utils.AppPermsLastAccessType int lastAccessType = summaryTimestamp.getSecond();
-
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access_yest,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
+ String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
}
}
- private void extractGroupUsageLastAccessTime(Map<String, Long> accessTime) {
- accessTime.clear();
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS), Instant.EPOCH.toEpochMilli());
-
- int numApps = mAppPermissionUsages.size();
- for (int appIndex = 0; appIndex < numApps; appIndex++) {
- AppPermissionUsage appUsage = mAppPermissionUsages.get(appIndex);
- String packageName = appUsage.getPackageName();
-
- List<AppPermissionUsage.GroupUsage> appGroups = appUsage.getGroupUsages();
- int numGroups = appGroups.size();
- for (int groupIndex = 0; groupIndex < numGroups; groupIndex++) {
- AppPermissionUsage.GroupUsage groupUsage = appGroups.get(groupIndex);
- String groupName = groupUsage.getGroup().getName();
- if (!mPermGroupName.equals(groupName)) {
- continue;
- }
-
- long lastAccessTime = groupUsage.getLastAccessTime();
- if (lastAccessTime == 0 || lastAccessTime < filterTimeBeginMillis) {
- continue;
- }
-
- String key = groupUsage.getGroup().getUser() + packageName;
- accessTime.put(key, lastAccessTime);
- }
- }
- }
private int comparePreference(Preference lhs, Preference rhs) {
int result = mCollator.compare(lhs.getTitle().toString(),
@@ -486,27 +423,8 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
-
- int category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
- if (isAllowed) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
- } else if (isAllowedForeground) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
- } else if (isDenied) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
- }
-
- Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(),
- packageName, user);
- if (uid == null) {
- return;
- }
-
- PermissionControllerStatsLog.write(PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
- mPermGroupName, uid, packageName, category);
- Log.v(LOG_TAG, "PermissionAppsFragment created with sessionId=" + sessionId
- + " permissionGroupName=" + mPermGroupName + " appUid="
- + uid + " packageName=" + packageName
- + " category=" + category);
+ mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+ isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+ mPermGroupName, LOG_TAG);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
index 05e7292fa..9ab0dc45c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
@@ -58,6 +58,7 @@ import com.android.permissioncontroller.PermissionControllerApplication;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
index e7fe255fa..c7beffa4f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
@@ -247,6 +247,7 @@ public class PermissionHistoryPreference extends Preference {
viewUsageIntent.putExtra(Intent.EXTRA_START_TIME,
mAccessTimeList.get(mAccessTimeList.size() - 1));
viewUsageIntent.putExtra(Intent.EXTRA_END_TIME, mAccessTimeList.get(0));
+ viewUsageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PackageManager packageManager = mContext.getPackageManager();
ResolveInfo resolveInfo = packageManager.resolveActivity(viewUsageIntent,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
index caeca2d65..acc39c24a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
@@ -52,6 +52,7 @@ import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
import com.android.permissioncontroller.permission.model.AppPermissionUsage.GroupUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
import com.android.permissioncontroller.permission.ui.handheld.PermissionUsageV2ControlPreference;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
index fa1ce2f17..6386e5150 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
@@ -19,6 +19,9 @@ package com.android.permissioncontroller.permission.ui.legacy;
import static android.content.Intent.ACTION_MANAGE_APP_PERMISSION;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
@@ -42,6 +45,7 @@ import com.android.permissioncontroller.permission.utils.LocationUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
+import java.util.Random;
/**
* Manage a single permission of a single app.
@@ -146,8 +150,13 @@ public final class AppPermissionActivity extends FragmentActivity {
if (DeviceUtils.isAuto(this)) {
Fragment androidXFragment;
+ long sessionId = getIntent().getLongExtra(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ while (sessionId == INVALID_SESSION_ID) {
+ sessionId = new Random().nextLong();
+ }
+
androidXFragment = AutoAppPermissionFragment.newInstance(packageName, permissionName,
- groupName, userHandle);
+ groupName, userHandle, sessionId);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
androidXFragment).commit();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
index afedeca5c..0aaf99bdd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
@@ -21,6 +21,8 @@ import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_IGNORED
import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+import android.apphibernation.AppHibernationManager
+import android.content.Context
import android.os.Bundle
import android.os.UserHandle
import android.util.Log
@@ -34,6 +36,7 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISS
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
import com.android.permissioncontroller.R
+import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData
@@ -42,13 +45,18 @@ import com.android.permissioncontroller.permission.data.PackagePermissionsLiveDa
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.model.AppPermissionUsage
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.ui.Category
import com.android.permissioncontroller.permission.utils.IPC
import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType
import com.android.permissioncontroller.permission.utils.navigateSafe
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
/**
* ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all
@@ -64,6 +72,7 @@ class AppPermissionGroupsViewModel(
) : ViewModel() {
companion object {
+ const val AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1
val LOG_TAG: String = AppPermissionGroupsViewModel::class.java.simpleName
}
@@ -209,6 +218,11 @@ class AppPermissionGroupsViewModel(
MODE_IGNORED
}
aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, mode)
+ if (isHibernationEnabled() && !enabled) {
+ val ahm = app.getSystemService(AppHibernationManager::class.java)!!
+ ahm.setHibernatingForUser(packageName, false)
+ ahm.setHibernatingGlobally(packageName, false)
+ }
}
}
}
@@ -220,6 +234,124 @@ class AppPermissionGroupsViewModel(
fun showAllPermissions(fragment: Fragment, args: Bundle) {
fragment.findNavController().navigateSafe(R.id.perm_groups_to_all_perms, args)
}
+
+ // This method should be consolidated with
+ // PermissionAppsViewModel#extractGroupUsageLastAccessTime
+ fun extractGroupUsageLastAccessTime(
+ accessTime: MutableMap<String, Long>,
+ appPermissionUsages: List<AppPermissionUsage>,
+ packageName: String
+ ) {
+ accessTime.clear()
+ val filterTimeBeginMillis = max(System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ val numApps: Int = appPermissionUsages.size
+ for (appIndex in 0 until numApps) {
+ val appUsage: AppPermissionUsage = appPermissionUsages[appIndex]
+ if (appUsage.packageName != packageName) {
+ continue
+ }
+ val appGroups = appUsage.groupUsages
+ val numGroups = appGroups.size
+ for (groupIndex in 0 until numGroups) {
+ val groupUsage = appGroups[groupIndex]
+ var lastAccessTime = groupUsage.lastAccessTime
+ val groupName = groupUsage.group.name
+ if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
+ continue
+ }
+
+ // We might have another AppPermissionUsage entry that's of the same packageName
+ // but with a different uid. In that case, we want to grab the max lastAccessTime
+ // as the last usage to show.
+ lastAccessTime = Math.max(
+ accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
+ lastAccessTime)
+ accessTime[groupName] = lastAccessTime
+ }
+ }
+ }
+
+ fun getPreferenceSummary(groupInfo: GroupUiInfo, context: Context, lastAccessTime: Long?):
+ String {
+ val summaryTimestamp = Utils
+ .getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime, context, groupInfo.groupName)
+ @AppPermsLastAccessType val lastAccessType: Int = summaryTimestamp.second
+
+ return when (groupInfo.subtitle) {
+ PermSubtitle.BACKGROUND ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_background)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_background,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_background,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_background)
+ else -> context.getString(
+ R.string.permission_subtitle_background)
+ }
+ PermSubtitle.MEDIA_ONLY ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_media_only)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_media_only,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_media_only,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_media_only)
+ else -> context.getString(R.string.permission_subtitle_media_only)
+ }
+ PermSubtitle.ALL_FILES ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_all_files)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_all_files,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_all_files,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_all_files)
+ else -> context.getString(R.string.permission_subtitle_all_files)
+ }
+ PermSubtitle.FOREGROUND_ONLY ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> ""
+ else -> ""
+ }
+ else ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> ""
+ else -> ""
+ }
+ }
+ }
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index 7fac5bad7..64549703f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -417,7 +417,8 @@ class GrantPermissionsViewModel(
// Show location permission dialogs based on location permissions
val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
- if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled()) {
+ if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() &&
+ packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
if (needFgPermissions) {
locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
if (fgState != null &&
@@ -433,6 +434,12 @@ class GrantPermissionsViewModel(
buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
}
} else {
+ if (!fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)) {
+ Log.e(LOG_TAG, "ACCESS_FINE_LOCATION must be requested " +
+ "with ACCESS_COARSE_LOCATION.")
+ value = null
+ return
+ }
if (coarseLocationPerm?.isOneTime == false &&
!coarseLocationPerm.isUserSet &&
!coarseLocationPerm.isUserFixed) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
index 8d4e8c21f..970569c31 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television
+package com.android.permissioncontroller.permission.ui.model
import android.app.Application
import androidx.lifecycle.AndroidViewModel
@@ -23,7 +23,6 @@ import androidx.lifecycle.MediatorLiveData
import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData
import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo
-import com.android.permissioncontroller.permission.ui.model.UsedCustomPermGroupNamesLiveData
/**
* A [androidx.lifecycle.ViewModel] for [ManagePermissionsFragment] and
@@ -35,6 +34,7 @@ import com.android.permissioncontroller.permission.ui.model.UsedCustomPermGroupN
* [ManagePermissionsViewModel].
*/
class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) {
+
private val standardPermGroupsLiveData: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
index c7a13dbab..c4473b1b5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
@@ -19,20 +19,29 @@ package com.android.permissioncontroller.permission.ui.model
import android.Manifest
import android.app.Application
import android.content.Intent
+import android.content.res.Resources
import android.os.Bundle
import android.os.UserHandle
+import android.util.Log
import androidx.fragment.app.Fragment
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.navigation.fragment.findNavController
+import androidx.preference.Preference
import androidx.savedstate.SavedStateRegistryOwner
+import com.android.permissioncontroller.PermissionControllerStatsLog
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData
+import com.android.permissioncontroller.permission.model.AppPermissionUsage
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.ui.Category
import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog
@@ -40,8 +49,14 @@ import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewMo
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.HAS_SYSTEM_APPS_KEY
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid
import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.navigateSafe
+import java.text.Collator
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
/**
* ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each
@@ -59,6 +74,7 @@ class PermissionAppsViewModel(
) : ViewModel() {
companion object {
+ const val AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1
internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem"
internal const val HAS_SYSTEM_APPS_KEY = "hasSystem"
internal const val SHOW_ALWAYS_ALLOWED = "showAlways"
@@ -240,6 +256,110 @@ class PermissionAppsViewModel(
fragment.findNavController().navigateSafe(R.id.perm_apps_to_app, args)
}
+
+ fun getFilterTimeBeginMillis(): Long {
+ return max(System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ }
+
+ /**
+ * Return a mapping of user + packageName to their last access timestamps for the permission
+ * group.
+ */
+ fun extractGroupUsageLastAccessTime(appPermissionUsages: List<AppPermissionUsage>):
+ MutableMap<String, Long> {
+ val accessTime: MutableMap<String, Long> = HashMap()
+ val now = System.currentTimeMillis()
+ val filterTimeBeginMillis = max(
+ now - TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ val numApps: Int = appPermissionUsages.size
+ for (appIndex in 0 until numApps) {
+ val appUsage: AppPermissionUsage = appPermissionUsages.get(appIndex)
+ val packageName = appUsage.packageName
+ val appGroups = appUsage.groupUsages
+ val numGroups = appGroups.size
+ for (groupIndex in 0 until numGroups) {
+ val groupUsage = appGroups[groupIndex]
+ val groupUsageGroupName = groupUsage.group.name
+ if (groupName != groupUsageGroupName) {
+ continue
+ }
+ val lastAccessTime = groupUsage.lastAccessTime
+ if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
+ continue
+ }
+ val key = groupUsage.group.user.toString() + packageName
+ accessTime[key] = lastAccessTime
+ }
+ }
+ return accessTime
+ }
+
+ /**
+ * Return the String preference summary based on the last access time.
+ */
+ fun getPreferenceSummary(res: Resources, summaryTimestamp: Pair<String, Int>): String {
+ return when (summaryTimestamp.second) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> res.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> res.getString(R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> res.getString(R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ else -> ""
+ }
+ }
+
+ /**
+ * Return two preferences to determine their ordering.
+ */
+ fun comparePreference(collator: Collator, lhs: Preference, rhs: Preference): Int {
+ var result: Int = collator.compare(lhs.title.toString(),
+ rhs.title.toString())
+ if (result == 0) {
+ result = lhs.key.compareTo(rhs.key)
+ }
+ return result
+ }
+
+ /**
+ * Log that the fragment was created.
+ */
+ fun logPermissionAppsFragmentCreated(
+ packageName: String,
+ user: UserHandle,
+ viewId: Long,
+ isAllowed: Boolean,
+ isAllowedForeground: Boolean,
+ isDenied: Boolean,
+ sessionId: Long,
+ application: Application,
+ permGroupName: String,
+ tag: String
+ ) {
+ var category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+ when {
+ isAllowed -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+ }
+ isAllowedForeground -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+ }
+ isDenied -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
+ }
+ }
+ val uid = getPackageUid(application,
+ packageName, user) ?: return
+ PermissionControllerStatsLog.write(
+ PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
+ permGroupName, uid, packageName, category)
+ Log.v(tag, tag + " created with sessionId=" + sessionId +
+ " permissionGroupName=" + permGroupName + " appUid=" + uid +
+ " packageName=" + packageName + " category=" + category)
+ }
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
index bb45470e2..b69fb66a7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
import android.content.Context;
import android.content.Intent;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
index a9578acfe..49a01efaf 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
import android.content.Context;
@@ -27,9 +27,16 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-final class PermissionGroupPreferenceUtils {
+/**
+ * Utils related to displaying permission groups in preferences.
+ */
+public final class PermissionGroupPreferenceUtils {
- static void updateGroupOfPermissionPreferences(Context context, PreferenceGroup preferenceGroup,
+ /**
+ * Update a {@link PreferenceGroup} with the specified permission groups.
+ */
+ public static void updateGroupOfPermissionPreferences(Context context,
+ PreferenceGroup preferenceGroup,
List<PermGroupPackagesUiInfo> permissionGroups) {
if (!(permissionGroups instanceof ArrayList)) {
permissionGroups = new ArrayList<>(permissionGroups);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
index 51af4bc1e..38a899b03 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
@@ -31,6 +31,8 @@ import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
index 6e13dfaa9..629cf3487 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
@@ -32,6 +32,8 @@ import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import java.util.List;