diff --git a/res-product/values-af/strings.xml b/res-product/values-af/strings.xml
index 71f680db696..e334e4b9aeb 100644
--- a/res-product/values-af/strings.xml
+++ b/res-product/values-af/strings.xml
@@ -52,8 +52,8 @@
"Gebruik jou gesig om jou foon te ontsluit, aankope te magtig, of by programme aan te meld."
"Gebruik jou gesig om jou tablet te ontsluit, aankope te magtig, of by programme aan te meld."
"Gebruik jou gesig om jou toestel te ontsluit, aankope te magtig, of by programme aan te meld."
- "Gebruik jou gesig om jou foon te ontsluit of vir stawing in apps, soos wanneer jy by apps aanmeld of \'n aankoop goedkeur"
- "Gebruik jou gesig om jou tablet te ontsluit of vir stawing in apps, soos wanneer jy by apps aanmeld of \'n aankoop goedkeur"
+ "Gebruik jou gesig om jou foon te ontsluit of vir stawing in apps, soos wanneer jy by apps aanmeld of ’n aankoop goedkeur"
+ "Gebruik jou gesig om jou tablet te ontsluit of vir stawing in apps, soos wanneer jy by apps aanmeld of ’n aankoop goedkeur"
"Gebruik jou gesig om jou toestel te ontsluit of vir stawing in apps, soos wanneer jy by apps aanmeld of ’n aankoop goedkeur"
"Laat jou kind toe om hul gesig te gebruik om hul foon te ontsluit"
"Laat jou kind toe om hul gesig te gebruik om hul tablet te ontsluit"
diff --git a/res-product/values-ar/strings.xml b/res-product/values-ar/strings.xml
index 4cebd2f2672..136f464c2da 100644
--- a/res-product/values-ar/strings.xml
+++ b/res-product/values-ar/strings.xml
@@ -368,7 +368,7 @@
"للاستماع إلى بث صوتي، عليك أولاً أن تربط بهذا الهاتف سماعات الرأس التي تتوافق مع LE Audio."
"للاستماع إلى بث صوتي، عليك أولاً أن تربط بهذا الجهاز اللوحي سماعات الرأس التي تتوافق مع LE Audio."
"للاستماع إلى بث صوتي، عليك أولاً أن تربط بهذا الجهاز سماعات الرأس التي تتوافق مع LE Audio."
- "لا تتوفّر في هذا الهاتف ميزة LE Audio، وهي مطلوبة لتشغيل البث الصوتي."
- "لا تتوفّر في هذا الجهاز اللوحي ميزة LE Audio، وهي مطلوبة لتشغيل البث الصوتي."
- "لا تتوفّر في هذا الجهاز ميزة LE Audio، وهي مطلوبة لتشغيل البث الصوتي."
+ "هذا الهاتف غير متوافق مع تطبيق LE Audio المطلوب لتشغيل البث الصوتي."
+ "هذا الجهاز اللوحي غير متوافق مع تطبيق LE Audio المطلوب لتشغيل البث الصوتي."
+ "هذا الجهاز غير متوافق مع تطبيق LE Audio المطلوب لتشغيل البث الصوتي."
diff --git a/res-product/values-be/strings.xml b/res-product/values-be/strings.xml
index 1de922c136f..2fe5f40492c 100644
--- a/res-product/values-be/strings.xml
+++ b/res-product/values-be/strings.xml
@@ -52,9 +52,9 @@
"Выкарыстоўвайце функцыю распазнавання твару для разблакіроўкі тэлефона, аўтарызацыі купляў і ўваходу ў праграмы."
"Выкарыстоўвайце твар для разблакіроўкі планшэта, аўтарызацыі купляў і ўваходу ў праграмы."
"Выкарыстоўвайце твар для разблакіроўкі прылады, аўтарызацыі купляў і ўваходу ў праграмы."
- "Выкарыстоўвайце распазнаванне твару для разблакіроўкі тэлефона і аўтэнтыфікацыі ў праграмах, напрыклад, для ўваходу ў праграмы і ўхвалення купляў"
- "Выкарыстоўвайце распазнаванне твару для разблакіроўкі планшэта і аўтэнтыфікацыі ў праграмах, напрыклад, для ўваходу ў праграмы і ўхвалення купляў"
- "Выкарыстоўвайце распазнаванне твару для разблакіроўкі прылады і аўтэнтыфікацыі ў праграмах, напрыклад, для ўваходу ў праграмы і ўхвалення купляў"
+ "Выкарыстоўвайце распазнаванне твару як спосаб разблакіроўкі тэлефона або аўтэнтыфікацыі ў праграмах, напрыклад для ўваходу ў праграмы або ўхвалення куплі"
+ "Выкарыстоўвайце распазнаванне твару як спосаб разблакіроўкі планшэта або аўтэнтыфікацыі ў праграмах, напрыклад для ўваходу ў праграмы або ўхвалення куплі"
+ "Выкарыстоўвайце распазнаванне твару як спосаб разблакіроўкі прылады або аўтэнтыфікацыі ў праграмах, напрыклад для ўваходу ў праграмы або ўхвалення куплі"
"Дазвольце свайму дзіцяці выкарыстоўваць твар для разблакіроўкі тэлефона"
"Дазвольце свайму дзіцяці выкарыстоўваць твар для разблакіроўкі планшэта"
"Дазвольце свайму дзіцяці выкарыстоўваць твар для разблакіроўкі прылады"
diff --git a/res-product/values-bn/strings.xml b/res-product/values-bn/strings.xml
index 5fabce960a7..d7e0e3a1f81 100644
--- a/res-product/values-bn/strings.xml
+++ b/res-product/values-bn/strings.xml
@@ -52,9 +52,9 @@
"আপনার ফোন আনলক, কেনাকাটা অনুমোদন বা অ্যাপগুলিতে সাইন-ইন করতে আপনার ফেস ব্যবহার করুন।"
"আপনার ট্যাবলেট আনলক, কেনাকাটা অনুমোদন বা অ্যাপে সাইন-ইন করতে আপনার ফেস ব্যবহার করুন।"
"আপনার ডিভাইস আনলক, কেনাকাটা অনুমোদন বা অ্যাপে সাইন-ইন করতে আপনার ফেস ব্যবহার করুন।"
- "আপনার ফোন আনলক করতে অথবা অ্যাপে যাচাইকরণের সময় আপনার ফেস ব্যবহার করুন, যেমন অ্যাপে সাইন-ইন করা ও কোনও কেনাকাটায় অনুমোদন দেওয়া"
- "আপনার ট্যাবলেট আনলক করতে অথবা অ্যাপে যাচাইকরণের সময় আপনার ফেস ব্যবহার করুন, যেমন অ্যাপে সাইন-ইন করা ও কোনও কেনাকাটায় অনুমোদন দেওয়া"
- "আপনার ডিভাইস আনলক করতে অথবা অ্যাপে যাচাইকরণের সময় আপনার ফেস ব্যবহার করুন, যেমন অ্যাপে সাইন-ইন করা ও কোনও কেনাকাটায় অনুমোদন দেওয়া"
+ "আপনার ফেস ব্যবহার করে ফোন আনলক অথবা অ্যাপে যাচাই করুন, যেমন অ্যাপে সাইন-ইন করা অথবা কেনাকাটা অনুমোদন করার সময়"
+ "আপনার ফেস ব্যবহার করে ট্যাবলেট আনলক অথবা অ্যাপে যাচাই করুন, যেমন অ্যাপে সাইন-ইন করা অথবা কেনাকাটা অনুমোদন করার সময়"
+ "আপনার ফেস ব্যবহার করে ডিভাইস আনলক অথবা অ্যাপে যাচাই করুন, যেমন অ্যাপে সাইন-ইন করা অথবা কেনাকাটা অনুমোদন করার সময়"
"নিজের ফোন আনলক করার জন্য আপনার সন্তানকে তার মুখ ব্যবহার করার অনুমতি দেওয়া"
"নিজের ট্যাবলেট আনলক করার জন্য আপনার সন্তানকে তার মুখ ব্যবহার করার অনুমতি দেওয়া"
"নিজের ডিভাইস আনলক করার জন্য আপনার সন্তানকে তার মুখ ব্যবহার করার অনুমতি দেওয়া"
@@ -368,7 +368,7 @@
"অডিও স্ট্রিম শুনতে, প্রথমে এমন হেডফোন কানেক্ট করুন যেটি এই ফোনের LE অডিওতে কাজ করে।"
"অডিও স্ট্রিম শুনতে, প্রথমে এমন হেডফোন কানেক্ট করুন যেটি এই ট্যাবলেটের LE অডিওতে কাজ করে।"
"অডিও স্ট্রিম শুনতে, প্রথমে এমন হেডফোন কানেক্ট করুন যেটি এই ডিভাইসের LE অডিওতে কাজ করে।"
- "এই ফোনে LE অডিও কাজ করে না, যেটি অডিও স্ট্রিম শুনতে প্রয়োজন।"
- "এই ট্যাবলেটে LE অডিও কাজ করে না, যেটি অডিও স্ট্রিম শুনতে প্রয়োজন।"
- "এই ডিভাইসে LE অডিও কাজ করে না, যেটি অডিও স্ট্রিম শুনতে প্রয়োজন।"
+ "এই ফোনে LE অডিও কাজ করে না, অডিও স্ট্রিম শোনার জন্য এটি প্রয়োজন।"
+ "এই ট্যাবলেটে LE অডিও কাজ করে না, অডিও স্ট্রিম শোনার জন্য এটি প্রয়োজন।"
+ "এই ডিভাইসে LE অডিও কাজ করে না, অডিও স্ট্রিম শোনার জন্য এটি প্রয়োজন।"
diff --git a/res-product/values-fa/strings.xml b/res-product/values-fa/strings.xml
index 90f12f3e9e6..7bfa002ce82 100644
--- a/res-product/values-fa/strings.xml
+++ b/res-product/values-fa/strings.xml
@@ -368,7 +368,7 @@
"برای گوش دادن به جاریسازی صوتی، ابتدا هدفونی را که از «صدای کممصرف» پشتیبانی میکند به این تلفن وصل کنید."
"برای گوش دادن به جاریسازی صوتی، ابتدا هدفونی را که از «صدای کممصرف» پشتیبانی میکند به این رایانه لوحی وصل کنید."
"برای گوش دادن به جاریسازی صوتی، ابتدا هدفونی را که از «صدای کممصرف» پشتیبانی میکند به این دستگاه وصل کنید."
- "این تلفن از «صدای کممصرف» پشتیبانی نمیکند. این ویژگی برای گوش دادن به جاریسازیهای صوتی لازم است."
- "این رایانه لوحی از «صدای کممصرف» پشتیبانی نمیکند. این ویژگی برای گوش دادن به جاریسازیهای صوتی لازم است."
- "این دستگاه از «صدای کممصرف» پشتیبانی نمیکند. این ویژگی برای گوش دادن به جاریسازیهای صوتی لازم است."
+ "این تلفن از «صدای کممصرف» که برای گوش دادن به جاریسازیهای صوتی لازم است پشتیبانی نمیکند."
+ "این رایانه لوحی از «صدای کممصرف» که برای گوش دادن به جاریسازیهای صوتی لازم است پشتیبانی نمیکند."
+ "این دستگاه از «صدای کممصرف» که برای گوش دادن به جاریسازیهای صوتی لازم است پشتیبانی نمیکند."
diff --git a/res-product/values-hi/strings.xml b/res-product/values-hi/strings.xml
index 72e0ad40363..2f9efd608f1 100644
--- a/res-product/values-hi/strings.xml
+++ b/res-product/values-hi/strings.xml
@@ -52,9 +52,9 @@
"अपने फ़ोन को अनलॉक करने, खरीदारी की मंज़ूरी देने या ऐप्लिकेशन में साइन इन करने के लिए अपने चेहरे का इस्तेमाल करें."
"अपने टैबलेट को अनलाॅक करने, खरीदारी की मंज़ूरी देने या ऐप्लिकेशन में साइन इन करने के लिए, अपने चेहरे का इस्तेमाल करें."
"अपने डिवाइस को अनलाॅक करने, खरीदारी को मंज़ूरी देने या ऐप्लिकेशन में साइन इन करने के लिए, अपने चेहरे का इस्तेमाल करें."
- "फ़ोन अनलॉक करने के अलावा, ऐप्लिकेशन में साइन इन करते या खरीदारी की मंज़ूरी देते समय पहचान की पुष्टि करने के लिए, अपने चेहरे का इस्तेमाल करें"
- "टैबलेट अनलॉक करने के अलावा, ऐप्लिकेशन में साइन इन करते या खरीदारी की मंज़ूरी देते समय पहचान की पुष्टि करने के लिए, अपने चेहरे का इस्तेमाल करें"
- "डिवाइस अनलॉक करने के अलावा, ऐप्लिकेशन में साइन इन करते या खरीदारी की मंज़ूरी देते समय पहचान की पुष्टि करने के लिए, अपने चेहरे का इस्तेमाल करें"
+ "फ़ोन अनलॉक करने के लिए, अपने चेहरे का इस्तेमाल करें. इसके अलावा, ऐप्लिकेशन में साइन इन करने, खरीदारी की मंज़ूरी देने वगैरह के लिए पहचान की पुष्टि करते समय भी, अपने चेहरे का इस्तेमाल करें"
+ "टैबलेट अनलॉक करने के लिए, अपने चेहरे का इस्तेमाल करें. इसके अलावा, ऐप्लिकेशन में साइन इन करने, खरीदारी की मंज़ूरी देने वगैरह के लिए पहचान की पुष्टि करते समय भी, अपने चेहरे का इस्तेमाल करें"
+ "डिवाइस अनलॉक करने के लिए, अपने चेहरे का इस्तेमाल करें. इसके अलावा, ऐप्लिकेशन में साइन इन करने, खरीदारी की मंज़ूरी देने वगैरह के लिए पहचान की पुष्टि करते समय भी, अपने चेहरे का इस्तेमाल करें"
"अपने बच्चे को अनुमति दें कि वह फ़ोन को अनलॉक करने के लिए, अपना चेहरा इस्तेमाल कर सके"
"अपने बच्चे को अनुमति दें कि वह टैबलेट को अनलॉक करने के लिए, अपना चेहरा इस्तेमाल कर सके"
"अपने बच्चे को अनुमति दें कि वह डिवाइस को अनलॉक करने के लिए, अपना चेहरा इस्तेमाल कर सके"
diff --git a/res-product/values-hr/strings.xml b/res-product/values-hr/strings.xml
index 649fb6df771..e9a9e64a526 100644
--- a/res-product/values-hr/strings.xml
+++ b/res-product/values-hr/strings.xml
@@ -368,7 +368,7 @@
"Da biste slušali audiostream, najprije povežite slušalice koje podržavaju LE Audio s ovim telefonom."
"Da biste slušali audiostream, najprije povežite slušalice koje podržavaju LE Audio s ovim tabletom."
"Da biste slušali audiostream, najprije povežite slušalice koje podržavaju LE Audio s ovim uređajem."
- "Ovaj telefon ne podržava LE Audio, što je potrebno za slušanje audiostreamova."
- "Ovaj tablet ne podržava LE Audio, što je potrebno za slušanje audiostreamova."
- "Ovaj uređaj ne podržava LE Audio, što je potrebno za slušanje audiostreamova."
+ "Ovaj telefon ne podržava aplikaciju LE Audio, koja je potrebna za slušanje audiostreamova."
+ "Ovaj tablet ne podržava aplikaciju LE Audio, koja je potrebna za slušanje audiostreamova."
+ "Ovaj uređaj ne podržava aplikaciju LE Audio, koja je potrebna za slušanje audiostreamova."
diff --git a/res-product/values-hu/strings.xml b/res-product/values-hu/strings.xml
index 7cc4c128b81..ae448157111 100644
--- a/res-product/values-hu/strings.xml
+++ b/res-product/values-hu/strings.xml
@@ -52,9 +52,9 @@
"Használja arcát telefonja feloldásához, vásárlások engedélyezéséhez és alkalmazásokba való bejelentkezéshez."
"Használja arcát táblagépe feloldásához, vásárlások engedélyezéséhez és alkalmazásokba való bejelentkezéshez."
"Használja arcát eszköze feloldásához, vásárlások engedélyezéséhez és alkalmazásokba való bejelentkezéshez."
- "Használhatja arcát telefonja zárolásának feloldásához vagy hitelesítéshez az alkalmazásokban (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)."
- "Használhatja arcát táblagépe zárolásának feloldásához vagy hitelesítéshez az alkalmazásokban (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)."
- "Használhatja arcát eszköze zárolásának feloldásához vagy hitelesítéshez az alkalmazásokban (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)."
+ "Használhatja arcát telefonja zárolásának feloldásához vagy az alkalmazásokban végzett hitelesítéshez (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)"
+ "Használhatja arcát táblagépe zárolásának feloldásához vagy az alkalmazásokban végzett hitelesítéshez (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)."
+ "Használhatja arcát eszköze zárolásának feloldásához vagy az alkalmazásokban végzett hitelesítéshez (például alkalmazásokba való bejelentkezés vagy vásárlás jóváhagyása során)"
"Engedélyezheti gyermekének, hogy az arcával oldja fel a telefonja zárolását."
"Engedélyezheti gyermekének, hogy az arcával oldja fel a táblagépe zárolását."
"Engedélyezheti gyermekének, hogy az arcával oldja fel az eszköze zárolását."
@@ -368,7 +368,7 @@
"Ha audiostreamet szeretne hallgatni, először csatlakoztasson alacsony energiaszintű hangátvitelt támogató fejhallgatót ehhez a telefonhoz."
"Ha audiostreamet szeretne hallgatni, először csatlakoztasson alacsony energiaszintű hangátvitelt támogató fejhallgatót ehhez a táblagéphez."
"Ha audiostreamet szeretne hallgatni, először csatlakoztasson alacsony energiaszintű hangátvitelt támogató fejhallgatót ehhez az eszközhöz."
- "Ez a telefon nem támogatja az Alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
- "Ez a táblagép nem támogatja az Alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
- "Ez az eszköz nem támogatja az Alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
+ "Ez a telefon nem támogatja az alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
+ "Ez a táblagép nem támogatja az alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
+ "Ez az eszköz nem támogatja az alacsony energiaszintű hangátvitelt, amelyre szükség van az audiostreamek hallgatásához."
diff --git a/res-product/values-ka/strings.xml b/res-product/values-ka/strings.xml
index ec7a1a7b33b..764d445764b 100644
--- a/res-product/values-ka/strings.xml
+++ b/res-product/values-ka/strings.xml
@@ -52,9 +52,9 @@
"გამოიყენეთ თქვენი სახე ტელეფონის განსაბლოკად, შენაძენების ავტორიზაციისთვის თუ აპებში შესასვლელად."
"გამოიყენეთ თქვენი სახე ტაბლეტის განსაბლოკად, შენაძენების ავტორიზაციისთვის თუ აპებში შესასვლელად."
"გამოიყენეთ თქვენი სახე მოწყობილობის განსაბლოკად, შენაძენების ავტორიზაციისთვის თუ აპებში შესასვლელად."
- "გამოიყენეთ სახე თქვენი ტელეფონის განსაბლოკად ან აპებში ავტორიზაციისთვის, ისეთ შემთხვევებში, როცა აპებში შედიხართ ან შეძენას ადასტურებთ"
- "გამოიყენეთ სახე თქვენი ტაბლეტის განსაბლოკად ან აპებში ავტორიზაციისთვის, ისეთ შემთხვევებში, როცა აპებში შედიხართ ან შეძენას ადასტურებთ"
- "გამოიყენეთ სახე თქვენი მოწყობილობის განსაბლოკად ან აპებში ავტორიზაციისთვის, ისეთ შემთხვევებში, როცა აპებში შედიხართ ან შეძენას ადასტურებთ"
+ "აპებში შესვლის ან შეძენის დადასტურებისას გამოიყენეთ სახე თქვენი ტელეფონის განსაბლოკად ან აპებში ავტორიზაციისთვის"
+ "აპებში შესვლის ან შეძენის დადასტურებისას გამოიყენეთ სახე თქვენი ტაბლეტის განსაბლოკად ან აპებში ავტორიზაციისთვის"
+ "აპებში შესვლის ან შეძენის დადასტურებისას გამოიყენეთ სახე თქვენი მოწყობილობის განსაბლოკად ან აპებში ავტორიზაციისთვის"
"მიეცით უფლება თქვენს ბავშვს, სახის მეშვეობით განბლოკოს თავისი ტელეფონი"
"მიეცით უფლება თქვენს ბავშვს, სახის მეშვეობით განბლოკოს თავისი ტაბლეტი"
"მიეცით უფლება თქვენს ბავშვს, სახის მეშვეობით განბლოკოს თავისი მოწყობილობა"
diff --git a/res-product/values-kn/strings.xml b/res-product/values-kn/strings.xml
index 4bd58877a5a..cd6178654f3 100644
--- a/res-product/values-kn/strings.xml
+++ b/res-product/values-kn/strings.xml
@@ -52,9 +52,9 @@
"ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು, ಖರೀದಿಗಳನ್ನು ದೃಢೀಕರಿಸಲು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ."
"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು, ಖರೀದಿಗಳನ್ನು ದೃಢೀಕರಿಸಲು ಅಥವಾ ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ."
"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು, ಖರೀದಿಗಳನ್ನು ದೃಢೀಕರಿಸಲು ಅಥವಾ ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ."
- "ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯೊಂದನ್ನು ಅನುಮೋದಿಸುವಾಗ ಆ್ಯಪ್ಗಳಲ್ಲಿನ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ"
- "ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯೊಂದನ್ನು ಅನುಮೋದಿಸುವಾಗ ಆ್ಯಪ್ಗಳಲ್ಲಿನ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ"
- "ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯೊಂದನ್ನು ಅನುಮೋದಿಸುವಾಗ ಆ್ಯಪ್ಗಳಲ್ಲಿನ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ"
+ "ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ಆ್ಯಪ್ಗಳಲ್ಲಿ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ, ಅಂದರೆ, ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯನ್ನು ಅನುಮೋದಿಸುವಾಗ"
+ "ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ಆ್ಯಪ್ಗಳಲ್ಲಿ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ, ಅಂದರೆ, ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯನ್ನು ಅನುಮೋದಿಸುವಾಗ"
+ "ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅಥವಾ ಆ್ಯಪ್ಗಳಲ್ಲಿ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ, ಅಂದರೆ, ನೀವು ಆ್ಯಪ್ಗಳಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡುವಾಗ ಅಥವಾ ಖರೀದಿಯನ್ನು ಅನುಮೋದಿಸುವಾಗ"
"ನಿಮ್ಮ ಮಗುವಿಗೆ ತಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅವರ ಮುಖವನ್ನು ಬಳಸಲು ಅನುಮತಿಸಿ"
"ನಿಮ್ಮ ಮಗುವಿಗೆ ತಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅವರ ಮುಖವನ್ನು ಬಳಸಲು ಅನುಮತಿಸಿ"
"ನಿಮ್ಮ ಮಗುವಿಗೆ ತಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅವರ ಮುಖವನ್ನು ಬಳಸಲು ಅನುಮತಿಸಿ"
@@ -369,6 +369,6 @@
"ಆಡಿಯೋ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಕೇಳಲು, ಮೊದಲು ಈ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ LE ಆಡಿಯೊವನ್ನು ಬೆಂಬಲಿಸುವ ಹೆಡ್ಫೋನ್ಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಿ."
"ಆಡಿಯೋ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಕೇಳಲು, ಮೊದಲು ಈ ಸಾಧನದಲ್ಲಿ LE ಆಡಿಯೋವನ್ನು ಬೆಂಬಲಿಸುವ ಹೆಡ್ಫೋನ್ಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಿ."
"ಆಡಿಯೊ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಕೇಳಲು ಅಗತ್ಯವಿರುವ LE ಆಡಿಯೊವನ್ನು ಈ ಫೋನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
- "ಆಡಿಯೊ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಕೇಳಲು ಅಗತ್ಯವಿರುವ LE ಆಡಿಯೊವನ್ನು ಈ ಟ್ಯಾಬ್ಲೆಟ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
- "ಆಡಿಯೊ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಕೇಳಲು ಅಗತ್ಯವಿರುವ LE ಆಡಿಯೊವನ್ನು ಈ ಸಾಧನವು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
+ "ಆಡಿಯೋ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಕೇಳಲು ಅಗತ್ಯವಿರುವ LE ಆಡಿಯೊವನ್ನು ಈ ಟ್ಯಾಬ್ಲೆಟ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
+ "ಆಡಿಯೋ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಕೇಳಲು ಅಗತ್ಯವಿರುವ LE ಆಡಿಯೊವನ್ನು ಈ ಸಾಧನವು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
diff --git a/res-product/values-ko/strings.xml b/res-product/values-ko/strings.xml
index 7f6af0a9ead..c173981162e 100644
--- a/res-product/values-ko/strings.xml
+++ b/res-product/values-ko/strings.xml
@@ -52,9 +52,9 @@
"얼굴로 휴대전화를 잠금 해제하거나 구매를 승인하거나 앱에 로그인하세요."
"태블릿을 잠금 해제하거나 구매를 승인하거나 앱에 로그인할 때 얼굴 인식을 사용합니다."
"기기를 잠금 해제하거나 구매를 승인하거나 앱에 로그인할 때 얼굴 인식을 사용합니다."
- "휴대전화를 잠금 해제하거나 앱에서 본인을 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
- "태블릿을 잠금 해제하거나 앱에서 본인을 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
- "기기를 잠금 해제하거나 앱에서 본인을 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
+ "휴대전화를 잠금 해제하거나 앱에서 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
+ "태블릿을 잠금 해제하거나 앱에서 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
+ "기기를 잠금 해제하거나 앱에서 인증할 때(예: 앱 로그인 또는 구매 승인 시) 얼굴 인식을 사용하세요"
"자녀가 얼굴 인식을 사용하여 휴대전화를 잠금 해제할 수 있도록 허용합니다."
"자녀가 얼굴 인식을 사용하여 태블릿을 잠금 해제할 수 있도록 허용합니다."
"자녀가 얼굴 인식을 사용하여 기기를 잠금 해제할 수 있도록 허용합니다."
diff --git a/res-product/values-mk/strings.xml b/res-product/values-mk/strings.xml
index 2b59f33c4e3..40a46012d36 100644
--- a/res-product/values-mk/strings.xml
+++ b/res-product/values-mk/strings.xml
@@ -52,7 +52,7 @@
"Користете го ликот за отклучување на телефонот, за одобрување купувања или за најавување на апликации."
"Користете го ликот за отклучување на таблетот, за одобрување купувања или за најавување на апликации."
"Користете го ликот за отклучување на уредот, за одобрување купувања или за најавување на апликации."
- "Користете го вашиот лик за да го отклучите телефонот или за автентикација во апликации, како на пр., кога се најавувате во апликациите или одобрувате купување"
+ "Користете го вашето лик за да го отклучите телефонот или за автентикација во апликации, како на пр., кога се најавувате во апликациите или одобрувате купување"
"Користете го вашиот лик за да го отклучите таблетот или за автентикација во апликациите, како на пр., кога се најавувате во апликациите или одобрувате купување"
"Користете го вашиот лик за да го отклучите уредот или за автентикација во апликациите, како на пр., кога се најавувате во апликациите или одобрувате купување"
"Дозволете му на вашето дете да го користи своето лице за да го отклучи својот телефон"
diff --git a/res-product/values-mr/strings.xml b/res-product/values-mr/strings.xml
index 6f3debab0b3..a210786f359 100644
--- a/res-product/values-mr/strings.xml
+++ b/res-product/values-mr/strings.xml
@@ -368,7 +368,7 @@
"ऑडिओ स्ट्रीम ऐकण्यासाठी, सर्वप्रथम LE ऑडिओ ला सपोर्ट करणारे हेडफोन या फोनशी कनेक्ट करा."
"ऑडिओ स्ट्रीम ऐकण्यासाठी, सर्वप्रथम LE ऑडिओ ला सपोर्ट करणारे हेडफोन या टॅबलेटशी कनेक्ट करा."
"ऑडिओ स्ट्रीम ऐकण्यासाठी, सर्वप्रथम LE ऑडिओ ला सपोर्ट करणारे हेडफोन या डिव्हाइसशी कनेक्ट करा."
- "हा फोन LE ऑडिओ ला सपोर्ट करत नाही, जो ऑडिओ स्ट्रीम ऐकण्यासाठी आवश्यक आहे."
- "हा टॅबलेट LE ऑडिओ ला सपोर्ट करत नाही, जो ऑडिओ स्ट्रीम ऐकण्यासाठी आवश्यक आहे."
+ "हा फोन LE ऑडिओ ला सपोर्ट करत नाही, जे ऑडिओ स्ट्रीम ऐकण्यासाठी आवश्यक आहे."
+ "हा टॅबलेट LE ऑडिओ ला सपोर्ट करत नाही, जे ऑडिओ स्ट्रीम ऐकण्यासाठी आवश्यक आहे."
"हे डिव्हाइस LE ऑडिओ ला सपोर्ट करत नाही, जे ऑडिओ स्ट्रीम ऐकण्यासाठी आवश्यक आहे."
diff --git a/res-product/values-ms/strings.xml b/res-product/values-ms/strings.xml
index 7864f5641a9..ec25e3d1eda 100644
--- a/res-product/values-ms/strings.xml
+++ b/res-product/values-ms/strings.xml
@@ -52,9 +52,9 @@
"Gunakan wajah anda untuk membuka kunci telefon, membenarkan pembelian atau log masuk ke apl."
"Gunakan wajah anda untuk membuka kunci tablet anda, mengizinkan pembelian atau log masuk ke apl."
"Gunakan wajah anda untuk membuka kunci peranti anda, mengizinkan pembelian atau log masuk ke apl."
- "Gunakan wajah anda untuk membuka kunci telefon anda atau untuk pengesahan pada apl, seperti ketika anda log masuk ke apl atau meluluskan pembelian"
- "Gunakan wajah anda untuk membuka kunci tablet anda atau untuk pengesahan pada apl, seperti ketika anda log masuk ke apl atau meluluskan pembelian"
- "Gunakan wajah anda untuk membuka kunci peranti anda atau untuk pengesahan pada apl, seperti ketika anda log masuk ke apl atau meluluskan pembelian"
+ "Gunakan wajah anda untuk membuka kunci telefon anda atau untuk pengesahan pada apl, misalnya ketika anda log masuk ke apl atau meluluskan pembelian"
+ "Gunakan wajah anda untuk membuka kunci tablet anda atau untuk pengesahan pada apl, misalnya ketika anda log masuk ke apl atau meluluskan pembelian"
+ "Gunakan wajah anda untuk membuka kunci peranti anda atau untuk pengesahan pada apl, misalnya ketika anda log masuk ke apl atau meluluskan pembelian"
"Benarkan anak anda menggunakan wajah mereka untuk membuka kunci telefon mereka"
"Benarkan anak anda menggunakan wajah mereka untuk membuka kunci tablet mereka"
"Benarkan anak anda menggunakan wajah mereka untuk membuka kunci peranti mereka"
diff --git a/res-product/values-ne/strings.xml b/res-product/values-ne/strings.xml
index 28e10047a95..fa40d6cf2cc 100644
--- a/res-product/values-ne/strings.xml
+++ b/res-product/values-ne/strings.xml
@@ -54,7 +54,7 @@
"आफ्नो डिभाइस अनलक गर्न, खरिद गर्ने अनुमति दिन वा एपहरूमा साइन इन गर्न आफ्नो अनुहार प्रयोग गर्नुहोस्।"
"फोन अनलक गर्न वा एपहरूमा आफ्नो पहिचान पुष्टि गर्न (जस्तै, एपहरूमा साइन इन गर्दा वा कुनै खरिद स्वीकृत गर्दा) आफ्नो अनुहार प्रयोग गर्नुहोस्"
"ट्याब्लेट अनलक गर्न वा एपहरूमा आफ्नो पहिचान पुष्टि गर्न (जस्तै, एपहरूमा साइन इन गर्दा वा कुनै खरिद स्वीकृत गर्दा) आफ्नो अनुहार प्रयोग गर्नुहोस्"
- "डिभाइस अनलक गर्न वा एपहरूमा आफ्नो पहिचान पुष्टि गर्न (जस्तै, एपहरूमा साइन इन गर्दा वा कुनै खरिद स्वीकृत गर्दा) आफ्नो अनुहार प्रयोग गर्नुहोस्"
+ "डिभाइस अनलक गर्न वा एपहरूमा आफ्नो पहिचान पुष्टि गर्न ( एपहरूमा साइन इन गर्दा वा कुनै खरिद स्वीकृत गर्दा) आफ्नो अनुहार प्रयोग गर्नुहोस्"
"तपाईंका बच्चालाई उनको अनुहार प्रयोग गरी उनको फोन अनलक गर्न दिनुहोस्"
"तपाईंका बच्चालाई उनको अनुहार प्रयोग गरी उनको ट्याब्लेट अनलक गर्न दिनुहोस्"
"तपाईंका बच्चालाई उनको अनुहार प्रयोग गरी उनको डिभाइस अनलक गर्न दिनुहोस्"
@@ -368,7 +368,7 @@
"अडियो स्ट्रिम सुन्न सर्वप्रथम यो फोनमा LE अडियो चल्ने हेडफोन कनेक्ट गर्नुहोस्।"
"अडियो स्ट्रिम सुन्न सर्वप्रथम यो ट्याब्लेटमा LE अडियो चल्ने हेडफोन कनेक्ट गर्नुहोस्।"
"अडियो स्ट्रिम सुन्न सर्वप्रथम यो डिभाइसमा LE अडियो चल्ने हेडफोन कनेक्ट गर्नुहोस्।"
- "यो फोनमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियोले काम गर्दैन।"
- "यो ट्याब्लेटमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियोले काम गर्दैन।"
- "यो डिभाइसमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियोले काम गर्दैन।"
+ "यो फोनमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियो चल्दैन।"
+ "यो ट्याब्लेटमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियो चल्दैन।"
+ "यो डिभाइसमा अडियो स्ट्रिम सुन्न आवश्यक पर्ने LE अडियो चल्दैन।"
diff --git a/res-product/values-ru/strings.xml b/res-product/values-ru/strings.xml
index 0c25783686f..f6d3ebbbbe2 100644
--- a/res-product/values-ru/strings.xml
+++ b/res-product/values-ru/strings.xml
@@ -368,7 +368,7 @@
"Для воспроизведения аудиопотока подключите к телефону наушники с поддержкой LE Audio."
"Для воспроизведения аудиопотока подключите к планшету наушники с поддержкой LE Audio."
"Для воспроизведения аудиопотока подключите к устройству наушники с поддержкой LE Audio."
- "Этот телефон не поддерживает стандарт LE Audio, который позволяет слушать аудио онлайн."
- "Этот планшет не поддерживает стандарт LE Audio, который позволяет слушать аудио онлайн."
- "Это устройство не поддерживает стандарт LE Audio, который позволяет слушать аудио онлайн."
+ "Этот телефон не поддерживает стандарт LE Audio, который обеспечивает беспроводную многопотоковую передачу аудио."
+ "Этот планшет не поддерживает стандарт LE Audio, который обеспечивает беспроводную многопотоковую передачу аудио."
+ "Это устройство не поддерживает стандарт LE Audio, который обеспечивает беспроводную многопотоковую передачу аудио."
diff --git a/res-product/values-si/strings.xml b/res-product/values-si/strings.xml
index abdd64a7bc3..e766758260b 100644
--- a/res-product/values-si/strings.xml
+++ b/res-product/values-si/strings.xml
@@ -52,9 +52,9 @@
"ඔබේ දුරකථනය අගුලු හැරීමට, මිලදී ගැනීම්වලට අවසර දීමට, හෝ යෙදුම්වලට පිරීමට ඔබේ මුහුණ භාවිත කරන්න."
"ඔබේ ටැබ්ලටය අගුළු හැරීමට, මිල දී ගැනීම්වලට අවසර දීමට, හෝ යෙදුම්වලට පිරීමට ඔබේ මුහුණ භාවිතා කරන්න."
"ඔබේ උපාංගය අගුළු හැරීමට, මිල දී ගැනීම්වලට අවසර දීමට, හෝ යෙදුම්වලට පිරීමට ඔබේ මුහුණ භාවිතා කරන්න."
- "ඔබේ දුරකථනය අගුළු හැරීමට හෝ ඔබ පුරන විට හෝ මිල දී ගැනීමක් අනුමත කරන විට වැනි, යෙදුම් තුළ ඔබේ අනන්යතාව සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
- "ඔබ යෙදුම් වෙත පුරන විට හෝ මිල දී ගැනීමක් අනුමත කරන විට වැනි ඔබේ ටැබ්ලටය අගුළු හැරීමට හෝ යෙදුම් තුළ සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
- "ඔබ යෙදුම් වෙත පුරන විට හෝ මිල දී ගැනීමක් අනුමත කරන විට වැනි, ඔබේ උපාංගය අගුළු හැරීමට හෝ යෙදුම් තුළ සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
+ "ඔබේ දුරකථනය අගුලු හැරීමට හෝ ඔබ පුරන විට හෝ මිලදී ගැනීමක් අනුමත කරන විට වැනි, යෙදුම් තුළ ඔබේ අනන්යතාව සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
+ "ඔබ යෙදුම් වෙත පුරන විට හෝ මිලදී ගැනීමක් අනුමත කරන විට වැනි ඔබේ ටැබ්ලටය අගුලු හැරීමට හෝ යෙදුම් තුළ සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
+ "ඔබ යෙදුම් වෙත පුරන විට හෝ මිලදී ගැනීමක් අනුමත කරන විට වැනි, ඔබේ උපාංගය අගුලු හැරීමට හෝ යෙදුම් තුළ සත්යාපනය සඳහා ඔබේ මුහුණ භාවිත කරන්න"
"ඔබේ දරුවාට තම දුරකථනය අගුලු හැරීමට තම මුහුණ යොදා ගැනීමට ඉඩ දෙන්න"
"ඔබේ දරුවාට තම ටැබ්ලටය අගුලු හැරීමට තම මුහුණ යොදා ගැනීමට ඉඩ දෙන්න"
"ඔබේ දරුවාට තම උපාංගය අගුලු හැරීමට තම මුහුණ යොදා ගැනීමට ඉඩ දෙන්න"
@@ -368,7 +368,7 @@
"ශ්රව්ය ප්රවාහයකට සවන් දීමට, පළමුව LE ශ්රව්ය සඳහා සහය දක්වන හෙඩ්ෆෝන් මෙම දුරකථනයට සම්බන්ධ කරන්න."
"ශ්රව්ය ප්රවාහයකට සවන් දීමට, පළමුව LE ශ්රව්ය සඳහා සහය දක්වන හෙඩ්ෆෝන් මෙම ටැබ්ලටයට සම්බන්ධ කරන්න."
"ශ්රව්ය ප්රවාහයකට සවන් දීමට, පළමුව LE ශ්රව්ය සඳහා සහය දක්වන හෙඩ්ෆෝන් මෙම උපාංගයට සම්බන්ධ කරන්න."
- "මෙම දුරකථනය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio සඳහා සහය නොදක්වයි."
- "මෙම ටැබ්ලටය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio සඳහා සහය නොදක්වයි."
- "මෙම උපාංගය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio සඳහා සහය නොදක්වයි."
+ "මෙම දුරකථනය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio හට සහය නොදක්වයි."
+ "මෙම ටැබ්ලටය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio හට සහය නොදක්වයි."
+ "මෙම උපාංගය ශ්රව්ය ප්රවාහවලට සවන් දීමට අවශ්ය වන LE Audio හට සය නොදක්වයි."
diff --git a/res-product/values-sk/strings.xml b/res-product/values-sk/strings.xml
index 7d5ce22486e..4ed4a115086 100644
--- a/res-product/values-sk/strings.xml
+++ b/res-product/values-sk/strings.xml
@@ -52,7 +52,7 @@
"Používajte tvár na odomykanie telefónu, schvaľovanie nákupov a prihlasovanie sa do aplikácií."
"Pomocou tváre môžete odomykať tablet, autorizovať nákupy a prihlasovať sa do aplikácií."
"Pomocou tváre môžete odomykať telefón, autorizovať nákupy a prihlasovať sa do aplikácií."
- "Tvárou môžete odomykať telefón alebo overovať svoju totožnosť v aplikáciách, napríklad pri prihlasovaní do aplikácií alebo schvaľovaní nákupov"
+ "Odomykajte telefón a overujte totožnosť v aplikáciách tvárou, napríklad pri prihlasovaní do aplikácií alebo schvaľovaní nákupov"
"Odomykajte tablet a overujte totožnosť v aplikáciách tvárou, napríklad pri prihlasovaní do aplikácií alebo schvaľovaní nákupov"
"Odomykajte zariadenie a overujte totožnosť v aplikáciách tvárou, napríklad pri prihlasovaní do aplikácií alebo schvaľovaní nákupov"
"Povoľte svojmu dieťaťu odomykať telefón tvárou"
diff --git a/res-product/values-sw/strings.xml b/res-product/values-sw/strings.xml
index 83d629f9380..dbf833ea537 100644
--- a/res-product/values-sw/strings.xml
+++ b/res-product/values-sw/strings.xml
@@ -52,9 +52,9 @@
"Tumia uso wako kufungua simu yako, kuidhinisha ununuzi au kuingia katika akaunti za programu."
"Tumia uso wako ili ufungue kompyuta kibao yako, uidhinishe ununuzi au uingie katika akaunti kwenye programu."
"Tumia uso wako ili ufungue kifaa chako, uidhinishe ununuzi au uingie katika akaunti kwenye programu."
- "Tumia uso wako kufungua simu yako au kwa uthibitishaji katika programu, kama vile unapoingia katika programu au unapoidhinisha ununuzi"
- "Tumia uso wako kufungua kishikwambi chako au kwa uthibitishaji katika programu, kama vile unapoingia katika akaunti kwenye programu au unapoidhinisha ununuzi"
- "Tumia uso wako kufungua kifaa chako au kwa uthibitishaji katika programu, kama vile unapoingia katika programu au unapoidhinisha ununuzi"
+ "Tumia uso wako kufungua simu yako au kuthibitishaji kwenye programu, kama vile unapoingia katika akaunti kwenye programu au unapoidhinisha ununuzi"
+ "Tumia uso wako kufungua kishikwambi chako au kuthibitisha kwenye programu, kama vile unapoingia katika akaunti kwenye programu au unapoidhinisha ununuzi"
+ "Tumia uso wako kufungua kifaa chako au kuthibitisha kwenye programu, kama vile unapoingia katika akaunti kwenye programu au unapoidhinisha ununuzi"
"Ruhusu mtoto wako atumie uso wake kufungua simu yake"
"Ruhusu mtoto wako atumie uso wake kufungua kompyuta kibao yake"
"Ruhusu mtoto wako atumie uso wake kufungua kifaa chake"
diff --git a/res-product/values-ta/strings.xml b/res-product/values-ta/strings.xml
index ca993c96374..1e8ee614800 100644
--- a/res-product/values-ta/strings.xml
+++ b/res-product/values-ta/strings.xml
@@ -52,12 +52,9 @@
"மொபைலை அன்லாக் செய்ய, வாங்குதல்களை அங்கீகரிக்க & ஆப்ஸில் உள்நுழைய, உங்கள் முகத்தைப் பயன்படுத்தலாம்."
"டேப்லெட்டை அன்லாக் செய்ய, பர்ச்சேஸ்களை அங்கீகரிக்க, ஆப்ஸில் உள்நுழைய உங்கள் முகத்தைப் பயன்படுத்தலாம்."
"சாதனத்தை அன்லாக் செய்ய, பர்ச்சேஸ்களை அங்கீகரிக்க, ஆப்ஸில் உள்நுழைய உங்கள் முகத்தைப் பயன்படுத்தலாம்."
-
-
-
-
-
-
+ "உங்கள் முகத்தைக் காட்டி மொபைலை அன்லாக் செய்யலாம் அல்லது ஆப்ஸில் உள்நுழைதல், பர்ச்சேஸை அங்கீகரித்தல் போன்ற செயல்பாடுகளின்போது அதைச் செய்வது நீங்கள்தான் என்பதை உறுதிப்படுத்தலாம்"
+ "உங்கள் முகத்தைக் காட்டி டேப்லெட்டை அன்லாக் செய்யலாம் அல்லது ஆப்ஸில் உள்நுழைதல், பர்ச்சேஸை அங்கீகரித்தல் போன்ற செயல்பாடுகளின்போது அதைச் செய்வது நீங்கள்தான் என்பதை உறுதிப்படுத்தலாம்"
+ "உங்கள் முகத்தைக் காட்டி சாதனத்தை அன்லாக் செய்யலாம் அல்லது ஆப்ஸில் உள்நுழைதல், பர்ச்சேஸை அங்கீகரித்தல் போன்ற செயல்பாடுகளின்போது அதைச் செய்வது நீங்கள்தான் என்பதை உறுதிப்படுத்தலாம்"
"உங்கள் பிள்ளை தனது முகத்தைப் பயன்படுத்தி மொபைலை அன்லாக் செய்ய அனுமதிக்கவும்"
"உங்கள் பிள்ளை தனது முகத்தைப் பயன்படுத்தி டேப்லெட்டை அன்லாக் செய்ய அனுமதிக்கவும்"
"உங்கள் பிள்ளை தனது முகத்தைப் பயன்படுத்திச் சாதனத்தை அன்லாக் செய்ய அனுமதிக்கவும்"
@@ -371,10 +368,7 @@
"ஆடியோ ஸ்ட்ரீமைக் கேட்க, LE ஆடியோவை ஆதரிக்கும் ஹெட்ஃபோன்களை இந்த மொபைலுடன் முதலில் இணைக்கவும்."
"ஆடியோ ஸ்ட்ரீமைக் கேட்க, LE ஆடியோவை ஆதரிக்கும் ஹெட்ஃபோன்களை இந்த டேப்லெட்டுடன் முதலில் இணைக்கவும்."
"ஆடியோ ஸ்ட்ரீமைக் கேட்க, LE ஆடியோவை ஆதரிக்கும் ஹெட்ஃபோன்களை இந்தச் சாதனத்துடன் முதலில் இணைக்கவும்."
-
-
-
-
-
-
+ "இந்த மொபைல் LE ஆடியோவை ஆதரிக்காது. ஆனால் ஆடியோ ஸ்ட்ரீம்களைக் கேட்க அது தேவை."
+ "இந்த டேப்லெட் LE ஆடியோவை ஆதரிக்காது. ஆனால் ஆடியோ ஸ்ட்ரீம்களைக் கேட்க அது தேவை."
+ "இந்தச் சாதனம் LE ஆடியோவை ஆதரிக்காது. ஆனால் ஆடியோ ஸ்ட்ரீம்களைக் கேட்க அது தேவை."
diff --git a/res-product/values-tl/strings.xml b/res-product/values-tl/strings.xml
index 0a3f36c63d7..565cc51b5f1 100644
--- a/res-product/values-tl/strings.xml
+++ b/res-product/values-tl/strings.xml
@@ -368,7 +368,7 @@
"Para makinig sa audio stream, magkonekta muna ng headphones na sumusuporta sa LE Audio sa teleponong ito."
"Para makinig sa audio stream, magkonekta muna ng headphones na sumusuporta sa LE Audio sa tablet na ito."
"Para makinig sa audio stream, magkonekta muna ng headphones na sumusuporta sa LE Audio sa device na ito."
- "Hindi sinusuportahan ng teleponong ito ang LE Audio, na kailangan para makinig sa mga audio stream."
- "Hindi sinusuportahan ng tablet na ito ang LE Audio, na kailangan para makinig sa mga audio stream."
- "Hindi sinusuportahan ng device na ito ang LE Audio, na kailangan para makinig sa mga audio stream."
+ "Hindi sinusuportahan ng teleponong ito ang LE Audio, na kailangan para makapakinig sa mga audio stream."
+ "Hindi sinusuportahan ng tablet na ito ang LE Audio, na kailangan para makapakinig sa mga audio stream."
+ "Hindi sinusuportahan ng device na ito ang LE Audio, na kailangan para makapakinig sa mga audio stream."
diff --git a/res-product/values-tr/strings.xml b/res-product/values-tr/strings.xml
index ef43bdb89c1..5b9e37f56dc 100644
--- a/res-product/values-tr/strings.xml
+++ b/res-product/values-tr/strings.xml
@@ -368,7 +368,7 @@
"Ses yayını dinlemek için önce LE Audio destekleyen bir kulaklığı bu telefona bağlayın."
"Ses yayını dinlemek için önce LE Audio destekleyen bir kulaklığı bu tablete bağlayın."
"Ses yayını dinlemek için önce LE Audio destekleyen bir kulaklığı bu cihaza bağlayın."
- "Bu telefon ses yayınlarını dinlemek için gerekli olan LE Audio\'yu desteklemiyor."
- "Bu tablet ses yayınlarını dinlemek için gerekli olan LE Audio\'yu desteklemiyor."
- "Bu cihaz, ses yayınlarını dinlemek için gerekli olan LE Audio\'yu desteklemiyor."
+ "Bu telefon, ses yayınlarını dinlemek için gereken LE Audio\'yu desteklemiyor."
+ "Bu tablet, ses yayınlarını dinlemek için gereken LE Audio\'yu desteklemiyor."
+ "Bu cihaz, ses yayınlarını dinlemek için gereken LE Audio\'yu desteklemiyor."
diff --git a/res-product/values-ur/strings.xml b/res-product/values-ur/strings.xml
index 5fa489ea9db..7b413ad5ccd 100644
--- a/res-product/values-ur/strings.xml
+++ b/res-product/values-ur/strings.xml
@@ -368,7 +368,7 @@
"آڈیو سلسلہ سننے کے لیے، پہلے ہیڈ فونز کو اس فون سے منسلک کریں جو LE آڈیو کو سپورٹ کرتے ہیں۔"
"آڈیو سلسلہ سننے کے لیے، پہلے ہیڈ فونز کو اس ٹیبلیٹ سے منسلک کریں جو LE آڈیو کو سپورٹ کرتے ہیں۔"
"آڈیو سلسلہ سننے کے لیے، پہلے ہیڈ فونز کو اس آلہ سے منسلک کریں جو LE آڈیو کو سپورٹ کرتے ہیں۔"
- "یہ فون LE آڈیو کو سپورٹ نہیں کرتا، جو آڈیو سلسلے کو سننے کے لیے درکار ہے۔"
- "یہ ٹیبلیٹ LE آڈیو کو سپورٹ نہیں کرتا، جو آڈیو سلسلے کو سننے کے لیے درکار ہے۔"
- "یہ آلہ LE آڈیو کو سپورٹ نہیں کرتا، جو آڈیو سلسلے کو سننے کے لیے درکار ہے۔"
+ "یہ فون LE آڈیو کا تعاون نہیں کرتا ہے، جو آڈیو سلسلوں کو سننے کیلئے درکار ہے۔"
+ "یہ ٹیبلیٹ LE آڈیو کا تعاون نہیں کرتا ہے، جو آڈیو سلسلوں کو سننے کیلئے درکار ہے۔"
+ "یہ آلہ LE آڈیو کا تعاون نہیں کرتا ہے، جو آڈیو سلسلوں کو سننے کیلئے ضروری ہے۔"
diff --git a/res-product/values-zu/strings.xml b/res-product/values-zu/strings.xml
index 525bcb67c8d..5ead8abb0ff 100644
--- a/res-product/values-zu/strings.xml
+++ b/res-product/values-zu/strings.xml
@@ -369,6 +369,6 @@
"Ukuze ulalele ukusakaza komsindo, qala uxhume ama-headphone asekela Umsindo we-LE kule thebulethi."
"Ukuze ulalele ukusakaza komsindo, qala uxhume ama-headphone asekela Umsindo we-LE kule divayisi."
"Le foni ayiwusekeli Umsindo we-LE, odingekayo ukuze ulalele ukusakazwa komsindo."
- "Le thebhulethi ayiwusekeli Umsindo we-LE, odingekayo ukuze ulalele ukusakazwa komsindo."
+ "Le thebhulethi ayiwusekeli Umsindo we-LE, odingekayo ukuze ulalele ukusakaza komsindo."
"Le divayisi ayiwusekeli Umsindo we-LE, odingekayo ukuze ulalele ukusakazwa komsindo."
diff --git a/res/layout/modes_activation_button.xml b/res/layout/modes_activation_button.xml
new file mode 100644
index 00000000000..e8ed824dfb4
--- /dev/null
+++ b/res/layout/modes_activation_button.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-af/arrays.xml b/res/values-af/arrays.xml
index d42585b44e3..882b5e005d0 100644
--- a/res/values-af/arrays.xml
+++ b/res/values-af/arrays.xml
@@ -495,9 +495,6 @@
- "Ná 1 minuut"
- "Ná 5 minute"
-
-
-
diff --git a/res/values-am/arrays.xml b/res/values-am/arrays.xml
index e6b26815c47..0380e61b7d4 100644
--- a/res/values-am/arrays.xml
+++ b/res/values-am/arrays.xml
@@ -495,9 +495,6 @@
- "ከ1 ደቂቃ በኋላ"
- "ከ5 ደቂቃዎች በኋላ"
-
-
-
diff --git a/res/values-ar/arrays.xml b/res/values-ar/arrays.xml
index 49f4972e50b..31c99705830 100644
--- a/res/values-ar/arrays.xml
+++ b/res/values-ar/arrays.xml
@@ -495,9 +495,6 @@
- "بعد دقيقة واحدة"
- "بعد 5 دقائق"
-
-
-
diff --git a/res/values-as/arrays.xml b/res/values-as/arrays.xml
index 833582e2d32..c414563ed01 100644
--- a/res/values-as/arrays.xml
+++ b/res/values-as/arrays.xml
@@ -495,9 +495,6 @@
- "১ মিনিটৰ পাছত"
- "৫ মিনিটৰ পাছত"
-
-
-
diff --git a/res/values-az/arrays.xml b/res/values-az/arrays.xml
index 2ea10991f17..93d30f788c4 100644
--- a/res/values-az/arrays.xml
+++ b/res/values-az/arrays.xml
@@ -495,9 +495,6 @@
- "1 dəqiqə sonra"
- "5 dəqiqə sonra"
-
-
-
diff --git a/res/values-b+sr+Latn/arrays.xml b/res/values-b+sr+Latn/arrays.xml
index 345ca3e2220..6dea39e545c 100644
--- a/res/values-b+sr+Latn/arrays.xml
+++ b/res/values-b+sr+Latn/arrays.xml
@@ -495,9 +495,6 @@
- "Posle 1 minuta"
- "Posle 5 minuta"
-
-
-
diff --git a/res/values-be/arrays.xml b/res/values-be/arrays.xml
index 0c87b24fddc..2a7b5295165 100644
--- a/res/values-be/arrays.xml
+++ b/res/values-be/arrays.xml
@@ -495,9 +495,6 @@
- "Праз 1 хвіліну"
- "Праз 5 хвілін"
-
-
-
diff --git a/res/values-bg/arrays.xml b/res/values-bg/arrays.xml
index 91ea84d3619..7726731c33c 100644
--- a/res/values-bg/arrays.xml
+++ b/res/values-bg/arrays.xml
@@ -495,9 +495,6 @@
- "След 1 минута"
- "След 5 минути"
-
-
-
diff --git a/res/values-bn/arrays.xml b/res/values-bn/arrays.xml
index 00fbdaa3863..c88de1c9e9b 100644
--- a/res/values-bn/arrays.xml
+++ b/res/values-bn/arrays.xml
@@ -495,9 +495,6 @@
- "১ মিনিট পর"
- "৫ মিনিট পর"
-
-
-
diff --git a/res/values-bs/arrays.xml b/res/values-bs/arrays.xml
index 9eef4cc7569..93ee2fe2799 100644
--- a/res/values-bs/arrays.xml
+++ b/res/values-bs/arrays.xml
@@ -495,9 +495,6 @@
- "Nakon 1 min"
- "Nakon 5 min"
-
-
-
diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml
index 92e74de270c..9b23023628c 100644
--- a/res/values-ca/arrays.xml
+++ b/res/values-ca/arrays.xml
@@ -495,9 +495,6 @@
- "Després d\'1 minut"
- "Després de 5 minuts"
-
-
-
diff --git a/res/values-cs/arrays.xml b/res/values-cs/arrays.xml
index 8f964244429..5296677c5de 100644
--- a/res/values-cs/arrays.xml
+++ b/res/values-cs/arrays.xml
@@ -495,9 +495,6 @@
- "Po 1 minutě"
- "Po 5 minutách"
-
-
-
diff --git a/res/values-da/arrays.xml b/res/values-da/arrays.xml
index 5ae7af88fb7..0664775834f 100644
--- a/res/values-da/arrays.xml
+++ b/res/values-da/arrays.xml
@@ -495,9 +495,6 @@
- "Efter 1 minut"
- "Efter 5 minutter"
-
-
-
diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml
index e6c556ca9bf..f5245a3a073 100644
--- a/res/values-de/arrays.xml
+++ b/res/values-de/arrays.xml
@@ -495,9 +495,6 @@
- "Nach 1 Minute"
- "Nach 5 Minuten"
-
-
-
diff --git a/res/values-el/arrays.xml b/res/values-el/arrays.xml
index e78f9ded6fb..23d07bd755d 100644
--- a/res/values-el/arrays.xml
+++ b/res/values-el/arrays.xml
@@ -495,9 +495,6 @@
- "Μετά από 1 λεπτό"
- "Μετά από 5 λεπτά"
-
-
-
diff --git a/res/values-en-rAU/arrays.xml b/res/values-en-rAU/arrays.xml
index 16a4d1c4edc..ab87841b2d3 100644
--- a/res/values-en-rAU/arrays.xml
+++ b/res/values-en-rAU/arrays.xml
@@ -495,9 +495,6 @@
- "After one minute"
- "After five minutes"
-
-
-
diff --git a/res/values-en-rCA/arrays.xml b/res/values-en-rCA/arrays.xml
index c91da100175..61ce5d27cc9 100644
--- a/res/values-en-rCA/arrays.xml
+++ b/res/values-en-rCA/arrays.xml
@@ -495,9 +495,6 @@
- "After 1 minute"
- "After 5 minutes"
-
-
-
diff --git a/res/values-en-rGB/arrays.xml b/res/values-en-rGB/arrays.xml
index 16a4d1c4edc..ab87841b2d3 100644
--- a/res/values-en-rGB/arrays.xml
+++ b/res/values-en-rGB/arrays.xml
@@ -495,9 +495,6 @@
- "After one minute"
- "After five minutes"
-
-
-
diff --git a/res/values-en-rIN/arrays.xml b/res/values-en-rIN/arrays.xml
index 16a4d1c4edc..ab87841b2d3 100644
--- a/res/values-en-rIN/arrays.xml
+++ b/res/values-en-rIN/arrays.xml
@@ -495,9 +495,6 @@
- "After one minute"
- "After five minutes"
-
-
-
diff --git a/res/values-en-rXC/arrays.xml b/res/values-en-rXC/arrays.xml
index 7c13b9389ee..2fed1f9cc54 100644
--- a/res/values-en-rXC/arrays.xml
+++ b/res/values-en-rXC/arrays.xml
@@ -495,9 +495,6 @@
- "After 1 minute"
- "After 5 minutes"
-
-
-
diff --git a/res/values-es-rUS/arrays.xml b/res/values-es-rUS/arrays.xml
index 7a8acebf344..d22996d2a03 100644
--- a/res/values-es-rUS/arrays.xml
+++ b/res/values-es-rUS/arrays.xml
@@ -338,8 +338,8 @@
- "Ninguno"
- "Contorno"
- "Sombra paralela"
- - "Elevado"
- - "Disminuido"
+ - "En relieve"
+ - "Hundido"
- "25 %"
@@ -495,9 +495,6 @@
- "Después de 1 minuto"
- "Después de 5 minutos"
-
-
-
diff --git a/res/values-es/arrays.xml b/res/values-es/arrays.xml
index 8cda489a222..d236131ef59 100644
--- a/res/values-es/arrays.xml
+++ b/res/values-es/arrays.xml
@@ -495,9 +495,6 @@
- "Tras 1 minuto"
- "Tras 5 minutos"
-
-
-
diff --git a/res/values-et/arrays.xml b/res/values-et/arrays.xml
index fd8aa1a1335..687895d69e6 100644
--- a/res/values-et/arrays.xml
+++ b/res/values-et/arrays.xml
@@ -495,9 +495,6 @@
- "1 minuti pärast"
- "5 minuti pärast"
-
-
-
diff --git a/res/values-eu/arrays.xml b/res/values-eu/arrays.xml
index 88baee57e75..221e2cd9b34 100644
--- a/res/values-eu/arrays.xml
+++ b/res/values-eu/arrays.xml
@@ -495,9 +495,6 @@
- "1 minutu igarotakoan"
- "5 minutu igarotakoan"
-
-
-
diff --git a/res/values-fa/arrays.xml b/res/values-fa/arrays.xml
index c245727e1b2..cea3bff4645 100644
--- a/res/values-fa/arrays.xml
+++ b/res/values-fa/arrays.xml
@@ -495,9 +495,6 @@
- "پساز ۱ دقیقه"
- "پساز ۵ دقیقه"
-
-
-
diff --git a/res/values-fi/arrays.xml b/res/values-fi/arrays.xml
index 090f74aed7e..3e9acdea475 100644
--- a/res/values-fi/arrays.xml
+++ b/res/values-fi/arrays.xml
@@ -495,9 +495,6 @@
- "1 minuutin kuluttua"
- "5 minuutin kuluttua"
-
-
-
diff --git a/res/values-fr-rCA/arrays.xml b/res/values-fr-rCA/arrays.xml
index 31b6227c420..e34b5fbc047 100644
--- a/res/values-fr-rCA/arrays.xml
+++ b/res/values-fr-rCA/arrays.xml
@@ -495,9 +495,6 @@
- "Après 1 minute"
- "Après 5 minutes"
-
-
-
diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml
index 03328052c08..3f2dabbdac1 100644
--- a/res/values-fr/arrays.xml
+++ b/res/values-fr/arrays.xml
@@ -495,9 +495,6 @@
- "Après 1 minute"
- "Après 5 minutes"
-
-
-
diff --git a/res/values-gl/arrays.xml b/res/values-gl/arrays.xml
index bfa9111acf5..1f4fe3fa938 100644
--- a/res/values-gl/arrays.xml
+++ b/res/values-gl/arrays.xml
@@ -495,9 +495,6 @@
- "Tras 1 minuto"
- "Tras 5 minutos"
-
-
-
diff --git a/res/values-gu/arrays.xml b/res/values-gu/arrays.xml
index cdc52c59299..33420db0633 100644
--- a/res/values-gu/arrays.xml
+++ b/res/values-gu/arrays.xml
@@ -495,9 +495,6 @@
- "1 મિનિટ પછી"
- "5 મિનિટ પછી"
-
-
-
diff --git a/res/values-hi/arrays.xml b/res/values-hi/arrays.xml
index 5ae2a6799af..f05608ac3c1 100644
--- a/res/values-hi/arrays.xml
+++ b/res/values-hi/arrays.xml
@@ -495,9 +495,6 @@
- "एक मिनट बाद"
- "पांच मिनट बाद"
-
-
-
diff --git a/res/values-hr/arrays.xml b/res/values-hr/arrays.xml
index c175a9b7c0d..a5e61bc0579 100644
--- a/res/values-hr/arrays.xml
+++ b/res/values-hr/arrays.xml
@@ -495,9 +495,6 @@
- "Nakon 1 minute"
- "Nakon 5 minuta"
-
-
-
diff --git a/res/values-hu/arrays.xml b/res/values-hu/arrays.xml
index 7b8d8c65e96..db7e4a486d2 100644
--- a/res/values-hu/arrays.xml
+++ b/res/values-hu/arrays.xml
@@ -495,9 +495,6 @@
- "1 perc után"
- "5 perc után"
-
-
-
diff --git a/res/values-hy/arrays.xml b/res/values-hy/arrays.xml
index 18adbd7ea98..1a8b95d039b 100644
--- a/res/values-hy/arrays.xml
+++ b/res/values-hy/arrays.xml
@@ -495,9 +495,6 @@
- "1 րոպեից"
- "5 րոպեից"
-
-
-
diff --git a/res/values-in/arrays.xml b/res/values-in/arrays.xml
index 4743ba8ac7b..e878f49a461 100644
--- a/res/values-in/arrays.xml
+++ b/res/values-in/arrays.xml
@@ -495,9 +495,6 @@
- "Setelah 1 menit"
- "Setelah 5 menit"
-
-
-
diff --git a/res/values-is/arrays.xml b/res/values-is/arrays.xml
index 8fb10b58543..f13f517addc 100644
--- a/res/values-is/arrays.xml
+++ b/res/values-is/arrays.xml
@@ -495,9 +495,6 @@
- "Eftir 1 mínútu"
- "Eftir 5 mínútur"
-
-
-
diff --git a/res/values-it/arrays.xml b/res/values-it/arrays.xml
index e2d7686a970..6b7b9fdbbe5 100644
--- a/res/values-it/arrays.xml
+++ b/res/values-it/arrays.xml
@@ -495,9 +495,6 @@
- "Dopo 1 minuto"
- "Dopo 5 minuti"
-
-
-
diff --git a/res/values-iw/arrays.xml b/res/values-iw/arrays.xml
index 23ba65bc68e..44c7e8039b1 100644
--- a/res/values-iw/arrays.xml
+++ b/res/values-iw/arrays.xml
@@ -495,9 +495,6 @@
- "לאחר דקה אחת"
- "לאחר 5 דקות"
-
-
-
diff --git a/res/values-ja/arrays.xml b/res/values-ja/arrays.xml
index 7a7abbc2644..ceafd68d8e1 100644
--- a/res/values-ja/arrays.xml
+++ b/res/values-ja/arrays.xml
@@ -495,9 +495,6 @@
- "1 分後"
- "5 分後"
-
-
-
diff --git a/res/values-ka/arrays.xml b/res/values-ka/arrays.xml
index 20a8aa21382..e70a3e32690 100644
--- a/res/values-ka/arrays.xml
+++ b/res/values-ka/arrays.xml
@@ -495,9 +495,6 @@
- "1 წუთის შემდეგ"
- "5 წუთის შემდეგ"
-
-
-
diff --git a/res/values-kk/arrays.xml b/res/values-kk/arrays.xml
index b5ab3d8cc07..ba44902945f 100644
--- a/res/values-kk/arrays.xml
+++ b/res/values-kk/arrays.xml
@@ -495,9 +495,6 @@
- "1 минуттан кейін"
- "5 минуттан кейін"
-
-
-
diff --git a/res/values-km/arrays.xml b/res/values-km/arrays.xml
index b7f79c194c3..2500cdd998d 100644
--- a/res/values-km/arrays.xml
+++ b/res/values-km/arrays.xml
@@ -495,9 +495,6 @@
- "បន្ទាប់ពី 1 នាទី"
- "បន្ទាប់ពី 5 នាទី"
-
-
-
diff --git a/res/values-kn/arrays.xml b/res/values-kn/arrays.xml
index cbf5aa33c07..f8f29307904 100644
--- a/res/values-kn/arrays.xml
+++ b/res/values-kn/arrays.xml
@@ -495,9 +495,6 @@
- "1 ನಿಮಿಷದ ನಂತರ"
- "5 ನಿಮಿಷಗಳ ನಂತರ"
-
-
-
diff --git a/res/values-ko/arrays.xml b/res/values-ko/arrays.xml
index 096bd0e8bb3..9edb91b7835 100644
--- a/res/values-ko/arrays.xml
+++ b/res/values-ko/arrays.xml
@@ -495,9 +495,6 @@
- "1분 후"
- "5분 후"
-
-
-
diff --git a/res/values-ky/arrays.xml b/res/values-ky/arrays.xml
index acccdfde37c..d63a29243b6 100644
--- a/res/values-ky/arrays.xml
+++ b/res/values-ky/arrays.xml
@@ -495,9 +495,6 @@
- "1 мүнөттөн кийин"
- "5 мүнөттөн кийин"
-
-
-
diff --git a/res/values-lo/arrays.xml b/res/values-lo/arrays.xml
index 41eb7cb79f2..225ac313a3c 100644
--- a/res/values-lo/arrays.xml
+++ b/res/values-lo/arrays.xml
@@ -495,9 +495,6 @@
- "ຫຼັງຈາກ 1 ນາທີ"
- "ຫຼັງຈາກ 5 ນາທີ"
-
-
-
diff --git a/res/values-lt/arrays.xml b/res/values-lt/arrays.xml
index f611c29ac0b..4560705a1f9 100644
--- a/res/values-lt/arrays.xml
+++ b/res/values-lt/arrays.xml
@@ -495,9 +495,6 @@
- "Po vienos minutės"
- "Po penkių minučių"
-
-
-
diff --git a/res/values-lv/arrays.xml b/res/values-lv/arrays.xml
index d6620b8e42c..a4c27de0d24 100644
--- a/res/values-lv/arrays.xml
+++ b/res/values-lv/arrays.xml
@@ -495,9 +495,6 @@
- "Pēc 1 minūtes"
- "Pēc 5 minūtēm"
-
-
-
diff --git a/res/values-mk/arrays.xml b/res/values-mk/arrays.xml
index e0e764b720a..b7e7e75054c 100644
--- a/res/values-mk/arrays.xml
+++ b/res/values-mk/arrays.xml
@@ -495,9 +495,6 @@
- "По 1 минута"
- "По 5 минути"
-
-
-
diff --git a/res/values-ml/arrays.xml b/res/values-ml/arrays.xml
index ea8907626db..2f1d67bf3ac 100644
--- a/res/values-ml/arrays.xml
+++ b/res/values-ml/arrays.xml
@@ -495,9 +495,6 @@
- "ഒരു മിനിറ്റിന് ശേഷം"
- "5 മിനിറ്റിന് ശേഷം"
-
-
-
diff --git a/res/values-mn/arrays.xml b/res/values-mn/arrays.xml
index 939211ddf4a..c9e210ced44 100644
--- a/res/values-mn/arrays.xml
+++ b/res/values-mn/arrays.xml
@@ -495,9 +495,6 @@
- "1 минутын дараа"
- "5 минутын дараа"
-
-
-
diff --git a/res/values-mr/arrays.xml b/res/values-mr/arrays.xml
index dc97f63e1ae..29f7ccc0bae 100644
--- a/res/values-mr/arrays.xml
+++ b/res/values-mr/arrays.xml
@@ -495,9 +495,6 @@
- "एका मिनिटानंतर"
- "पाच मिनिटांनंतर"
-
-
-
diff --git a/res/values-ms/arrays.xml b/res/values-ms/arrays.xml
index 8256a192c76..62c74498985 100644
--- a/res/values-ms/arrays.xml
+++ b/res/values-ms/arrays.xml
@@ -495,9 +495,6 @@
- "Selepas 1 minit"
- "Selepas 5 minit"
-
-
-
diff --git a/res/values-my/arrays.xml b/res/values-my/arrays.xml
index 4009b19379e..939b5aeecae 100644
--- a/res/values-my/arrays.xml
+++ b/res/values-my/arrays.xml
@@ -495,9 +495,6 @@
- "၁ မိနစ်နောက်ပိုင်း"
- "၅ မိနစ်နောက်ပိုင်း"
-
-
-
diff --git a/res/values-nb/arrays.xml b/res/values-nb/arrays.xml
index f8e50bb64e7..65d7eccb3e8 100644
--- a/res/values-nb/arrays.xml
+++ b/res/values-nb/arrays.xml
@@ -495,9 +495,6 @@
- "Etter 1 minutt"
- "Etter 5 minutter"
-
-
-
diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml
index c1ae3238e91..49344cad94d 100644
--- a/res/values-ne/arrays.xml
+++ b/res/values-ne/arrays.xml
@@ -495,9 +495,6 @@
- "१ मिनेटपछि"
- "५ मिनेटपछि"
-
-
-
diff --git a/res/values-nl/arrays.xml b/res/values-nl/arrays.xml
index 9e98883a4d2..dd2b800eb0b 100644
--- a/res/values-nl/arrays.xml
+++ b/res/values-nl/arrays.xml
@@ -495,9 +495,6 @@
- "Na 1 minuut"
- "Na 5 minuten"
-
-
-
diff --git a/res/values-or/arrays.xml b/res/values-or/arrays.xml
index 3eb6110db39..65f977e4155 100644
--- a/res/values-or/arrays.xml
+++ b/res/values-or/arrays.xml
@@ -495,9 +495,6 @@
- "1 ମିନିଟ ପରେ"
- "5 ମିନିଟ ପରେ"
-
-
-
diff --git a/res/values-pa/arrays.xml b/res/values-pa/arrays.xml
index bd1f508f5dc..679294d23fb 100644
--- a/res/values-pa/arrays.xml
+++ b/res/values-pa/arrays.xml
@@ -495,9 +495,6 @@
- "1 ਮਿੰਟ ਬਾਅਦ"
- "5 ਮਿੰਟਾਂ ਬਾਅਦ"
-
-
-
diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml
index 8fa07b912fc..48e71789e2a 100644
--- a/res/values-pl/arrays.xml
+++ b/res/values-pl/arrays.xml
@@ -495,9 +495,6 @@
- "Po minucie"
- "Po 5 minutach"
-
-
-
diff --git a/res/values-pt-rBR/arrays.xml b/res/values-pt-rBR/arrays.xml
index 46c72052392..b3bfd9945e5 100644
--- a/res/values-pt-rBR/arrays.xml
+++ b/res/values-pt-rBR/arrays.xml
@@ -495,9 +495,6 @@
- "Depois de um minuto"
- "Depois de cinco minutos"
-
-
-
diff --git a/res/values-pt-rPT/arrays.xml b/res/values-pt-rPT/arrays.xml
index 0630fa203a4..c08f8c8a6e9 100644
--- a/res/values-pt-rPT/arrays.xml
+++ b/res/values-pt-rPT/arrays.xml
@@ -495,9 +495,6 @@
- "Após 1 minuto"
- "Após 5 minutos"
-
-
-
diff --git a/res/values-pt/arrays.xml b/res/values-pt/arrays.xml
index 46c72052392..b3bfd9945e5 100644
--- a/res/values-pt/arrays.xml
+++ b/res/values-pt/arrays.xml
@@ -495,9 +495,6 @@
- "Depois de um minuto"
- "Depois de cinco minutos"
-
-
-
diff --git a/res/values-ro/arrays.xml b/res/values-ro/arrays.xml
index 0fbd395f4ff..ba9b1d25262 100644
--- a/res/values-ro/arrays.xml
+++ b/res/values-ro/arrays.xml
@@ -495,9 +495,6 @@
- "După un minut"
- "După cinci minute"
-
-
-
diff --git a/res/values-ru/arrays.xml b/res/values-ru/arrays.xml
index 37dcc420783..9f7d302c70f 100644
--- a/res/values-ru/arrays.xml
+++ b/res/values-ru/arrays.xml
@@ -495,9 +495,6 @@
- "Через 1 минуту"
- "Через 5 минут"
-
-
-
diff --git a/res/values-si/arrays.xml b/res/values-si/arrays.xml
index fd459cc0c58..d51a1c03a1a 100644
--- a/res/values-si/arrays.xml
+++ b/res/values-si/arrays.xml
@@ -495,9 +495,6 @@
- "මිනිත්තු 1කට පසුව"
- "මිනිත්තු 5කට පසුව"
-
-
-
diff --git a/res/values-sk/arrays.xml b/res/values-sk/arrays.xml
index a3805ecc6c0..f314f3f16ad 100644
--- a/res/values-sk/arrays.xml
+++ b/res/values-sk/arrays.xml
@@ -495,9 +495,6 @@
- "Po 1 minúte"
- "Po 5 minútach"
-
-
-
diff --git a/res/values-sl/arrays.xml b/res/values-sl/arrays.xml
index 0bcf4d695a8..7257ead7adb 100644
--- a/res/values-sl/arrays.xml
+++ b/res/values-sl/arrays.xml
@@ -495,9 +495,6 @@
- "Po 1 minuti"
- "Po 5 minutah"
-
-
-
diff --git a/res/values-sq/arrays.xml b/res/values-sq/arrays.xml
index fb5ef1602b8..c1adabad735 100644
--- a/res/values-sq/arrays.xml
+++ b/res/values-sq/arrays.xml
@@ -495,9 +495,6 @@
- "Pas 1 minute"
- "Pas 5 minutash"
-
-
-
diff --git a/res/values-sr/arrays.xml b/res/values-sr/arrays.xml
index 4a478bbe142..ad421a1ccfe 100644
--- a/res/values-sr/arrays.xml
+++ b/res/values-sr/arrays.xml
@@ -495,9 +495,6 @@
- "После 1 минута"
- "После 5 минута"
-
-
-
diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml
index 3ee9288e0f6..c7693ec61ec 100644
--- a/res/values-sv/arrays.xml
+++ b/res/values-sv/arrays.xml
@@ -495,9 +495,6 @@
- "Efter 1 minut"
- "Efter 5 minuter"
-
-
-
diff --git a/res/values-sw/arrays.xml b/res/values-sw/arrays.xml
index 34c79dadcfb..2bfad672c8e 100644
--- a/res/values-sw/arrays.xml
+++ b/res/values-sw/arrays.xml
@@ -495,9 +495,6 @@
- "Baada ya dakika 1"
- "Baada ya dakika 5"
-
-
-
diff --git a/res/values-ta/arrays.xml b/res/values-ta/arrays.xml
index 0842bade491..7d0071333e0 100644
--- a/res/values-ta/arrays.xml
+++ b/res/values-ta/arrays.xml
@@ -495,9 +495,6 @@
- "1 நிமிடத்திற்குப் பிறகு"
- "5 நிமிடங்களுக்குப் பிறகு"
-
-
-
diff --git a/res/values-te/arrays.xml b/res/values-te/arrays.xml
index ee577c8a7e4..81b0df0b81c 100644
--- a/res/values-te/arrays.xml
+++ b/res/values-te/arrays.xml
@@ -495,9 +495,6 @@
- "1 నిమిషం తర్వాత"
- "5 నిమిషాల తర్వాత"
-
-
-
diff --git a/res/values-th/arrays.xml b/res/values-th/arrays.xml
index 67879ad74ba..e5367c18caa 100644
--- a/res/values-th/arrays.xml
+++ b/res/values-th/arrays.xml
@@ -495,9 +495,6 @@
- "หลังจาก 1 นาที"
- "หลังจาก 5 นาที"
-
-
-
diff --git a/res/values-tl/arrays.xml b/res/values-tl/arrays.xml
index f10838098b5..13711b4441b 100644
--- a/res/values-tl/arrays.xml
+++ b/res/values-tl/arrays.xml
@@ -495,9 +495,6 @@
- "Pagkalipas ng 1 minuto"
- "Pagkalipas ng 5 minuto"
-
-
-
diff --git a/res/values-tr/arrays.xml b/res/values-tr/arrays.xml
index a91db03902e..0f4147200f9 100644
--- a/res/values-tr/arrays.xml
+++ b/res/values-tr/arrays.xml
@@ -495,9 +495,6 @@
- "1 dakika sonra"
- "5 dakika sonra"
-
-
-
diff --git a/res/values-uk/arrays.xml b/res/values-uk/arrays.xml
index 41498c7c7f4..cf72bb8c655 100644
--- a/res/values-uk/arrays.xml
+++ b/res/values-uk/arrays.xml
@@ -495,9 +495,6 @@
- "Через 1 хвилину"
- "Через 5 хвилин"
-
-
-
diff --git a/res/values-ur/arrays.xml b/res/values-ur/arrays.xml
index 2bc5a6819da..bf3531d9ed0 100644
--- a/res/values-ur/arrays.xml
+++ b/res/values-ur/arrays.xml
@@ -495,9 +495,6 @@
- "1 منٹ کے بعد"
- "5 منٹ کے بعد"
-
-
-
diff --git a/res/values-uz/arrays.xml b/res/values-uz/arrays.xml
index 84a1def44d1..37e6546d90f 100644
--- a/res/values-uz/arrays.xml
+++ b/res/values-uz/arrays.xml
@@ -495,9 +495,6 @@
- "1 daqiqadan keyin"
- "5 daqiqadan keyin"
-
-
-
diff --git a/res/values-vi/arrays.xml b/res/values-vi/arrays.xml
index 067eb535f7a..3f83b3d4f26 100644
--- a/res/values-vi/arrays.xml
+++ b/res/values-vi/arrays.xml
@@ -495,9 +495,6 @@
- "Sau 1 phút"
- "Sau 5 phút"
-
-
-
diff --git a/res/values-zh-rCN/arrays.xml b/res/values-zh-rCN/arrays.xml
index b018d6a737f..486d9f7a42c 100644
--- a/res/values-zh-rCN/arrays.xml
+++ b/res/values-zh-rCN/arrays.xml
@@ -495,9 +495,6 @@
- "1 分钟后"
- "5 分钟后"
-
-
-
diff --git a/res/values-zh-rHK/arrays.xml b/res/values-zh-rHK/arrays.xml
index e400c32cc93..221f39a78a7 100644
--- a/res/values-zh-rHK/arrays.xml
+++ b/res/values-zh-rHK/arrays.xml
@@ -495,9 +495,6 @@
- "1 分鐘後"
- "5 分鐘後"
-
-
-
diff --git a/res/values-zh-rTW/arrays.xml b/res/values-zh-rTW/arrays.xml
index bfedd437a36..7eccb5cd22f 100644
--- a/res/values-zh-rTW/arrays.xml
+++ b/res/values-zh-rTW/arrays.xml
@@ -495,9 +495,6 @@
- "1 分鐘後"
- "5 分鐘後"
-
-
-
diff --git a/res/values-zu/arrays.xml b/res/values-zu/arrays.xml
index 3b45d387bdd..b3ba93bbc7c 100644
--- a/res/values-zu/arrays.xml
+++ b/res/values-zu/arrays.xml
@@ -495,9 +495,6 @@
- "Ngemuva komzuzu o-1"
- "Ngemuva kwemizuzu emi-5"
-
-
-
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3e097b87726..bc573228030 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1262,6 +1262,8 @@
To unlock private space, your eyes must be open. For best results, take off sunglasses.
Use your face to unlock your private space.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour private space can be unlocked by someone else if your device is held up to your face.\n\nYour private space can be unlocked by someone who looks a lot like you, like an identical sibling.
+
+ When using Face Unlock, always require confirmation step
Ways to unlock
@@ -6480,7 +6482,7 @@
No available trust agents
-
+
Activate device admin app?
Activate this device admin app
@@ -7718,7 +7720,7 @@
background, theme, grid, customize, personalize
- icon, accent, color, home screen, lock screen, shortcut, clock size
+ icon, icons, accent, color, home screen, lock screen, shortcut, clock size
default, assistant
@@ -8018,6 +8020,7 @@
}
+
Display options for filtered
notifications
@@ -9173,6 +9176,17 @@
Events
+
+ Apps
+
+ Apps that can interrupt
+
+ Selected apps
+
+ None
+
+ All
+
Allow apps to override
diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml
index a380987d377..df560957036 100644
--- a/res/xml/modes_rule_settings.xml
+++ b/res/xml/modes_rule_settings.xml
@@ -22,6 +22,11 @@
android:key="header"
android:layout="@layout/settings_entity_header" />
+
+
@@ -29,6 +34,10 @@
android:key="zen_mode_people"
android:title="@string/zen_category_people"/>
+
+
diff --git a/res/xml/zen_mode_apps_settings.xml b/res/xml/zen_mode_apps_settings.xml
new file mode 100644
index 00000000000..4ee14e41c5e
--- /dev/null
+++ b/res/xml/zen_mode_apps_settings.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/zen_mode_select_bypassing_apps.xml b/res/xml/zen_mode_select_bypassing_apps.xml
new file mode 100644
index 00000000000..7b5a84d131f
--- /dev/null
+++ b/res/xml/zen_mode_select_bypassing_apps.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java
index b5ddd1aeb96..042c5c26c1a 100644
--- a/src/com/android/settings/MainClearConfirm.java
+++ b/src/com/android/settings/MainClearConfirm.java
@@ -151,15 +151,20 @@ public class MainClearConfirm extends InstrumentedFragment {
if (pdbManager == null) {
return false;
}
+
// The persistent data block will persist if the device is still being provisioned.
if (isDeviceStillBeingProvisioned()) {
return false;
}
- // If OEM unlock is allowed, the persistent data block will be wiped during FR
- // process. If disabled, it will be wiped here instead.
- if (isOemUnlockedAllowed()) {
+
+ // If OEM unlock is allowed, the persistent data block will be wiped during the FR
+ // process on devices without FRP Hardening. If disabled, it will be wiped here instead.
+ // On devices with FRP Hardening, the persistent data block should always be wiped,
+ // regardless of the OEM Unlocking state.
+ if (!android.security.Flags.frpEnforcement() && isOemUnlockedAllowed()) {
return false;
}
+
final DevicePolicyManager dpm = (DevicePolicyManager) getActivity()
.getSystemService(Context.DEVICE_POLICY_SERVICE);
// Do not erase the factory reset protection data (from Settings) if factory reset
@@ -167,6 +172,7 @@ public class MainClearConfirm extends InstrumentedFragment {
if (!dpm.isFactoryResetProtectionPolicySupported()) {
return false;
}
+
// Do not erase the factory reset protection data (from Settings) if the
// device is an organization-owned managed profile device and a factory
// reset protection policy has been set.
@@ -175,6 +181,7 @@ public class MainClearConfirm extends InstrumentedFragment {
&& frpPolicy.isNotEmpty()) {
return false;
}
+
return true;
}
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
index cad21868e06..0f551b0fa1f 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
@@ -97,13 +97,12 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
@Override
protected ComponentName getTileComponentName() {
- // Don't have quick settings tile for now.
- return null;
+ return AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME;
}
@Override
protected CharSequence getTileTooltipContent(int type) {
- // Don't have quick settings tile for now.
+ // No tooltip to be shown
return null;
}
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 2295dee8272..4e77243b09d 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -210,7 +210,6 @@ public class AccessibilitySettings extends DashboardFragment implements
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
initializeAllPreferences();
- updateAllPreferences();
registerContentMonitors();
registerInputDeviceListener();
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
index f71e2febccd..d8205339c67 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
@@ -26,6 +26,7 @@ import android.provider.Settings;
import androidx.preference.Preference;
+import com.android.settings.R;
import com.android.settings.Utils;
import java.util.List;
@@ -76,6 +77,11 @@ public class FaceSettingsConfirmPreferenceController extends FaceSettingsPrefere
preference.setEnabled(false);
} else {
preference.setEnabled(true);
+ // Update summary for private space face settings toggle
+ if (Utils.isPrivateProfile(getUserId(), mContext)) {
+ preference.setSummary(mContext.getString(
+ R.string.private_space_face_settings_require_confirmation_details));
+ }
}
}
diff --git a/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateController.kt b/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateController.kt
deleted file mode 100644
index 3d17ec0d9ce..00000000000
--- a/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateController.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.deviceinfo.simstatus
-
-import android.content.Context
-import android.telephony.SubscriptionManager
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.settings.network.telephony.SimSlotRepository
-import com.android.settings.network.telephony.ims.ImsMmTelRepository
-import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.conflate
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.launch
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class ImsRegistrationStateController @JvmOverloads constructor(
- private val context: Context,
- private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
- private val imsMmTelRepositoryFactory: (subId: Int) -> ImsMmTelRepository = { subId ->
- ImsMmTelRepositoryImpl(context, subId)
- },
-) {
- fun collectImsRegistered(
- lifecycleOwner: LifecycleOwner,
- simSlotIndex: Int,
- action: (imsRegistered: Boolean) -> Unit,
- ) {
- lifecycleOwner.lifecycleScope.launch {
- lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
- imsRegisteredFlow(simSlotIndex).collect(action)
- }
- }
- }
-
- private fun imsRegisteredFlow(simSlotIndex: Int): Flow =
- simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
- .flatMapLatest { subId ->
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- imsMmTelRepositoryFactory(subId).imsRegisteredFlow()
- } else {
- flowOf(false)
- }
- }
- .conflate()
- .flowOn(Dispatchers.Default)
-}
diff --git a/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepository.kt b/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepository.kt
new file mode 100644
index 00000000000..8b062e70e03
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepository.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.simstatus
+
+import android.content.Context
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyCallback
+import android.util.Log
+import com.android.settings.R
+import com.android.settings.network.telephony.serviceStateFlow
+import com.android.settings.network.telephony.telephonyCallbackFlow
+import com.android.settingslib.Utils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class SignalStrengthRepository(
+ private val context: Context,
+ private val serviceStateFlowFactory: (subId: Int) -> Flow = {
+ context.serviceStateFlow(it)
+ },
+) {
+ fun signalStrengthDisplayFlow(subId: Int): Flow =
+ serviceStateFlowFactory(subId).flatMapLatest { serviceState ->
+ if (Utils.isInService(serviceState)) {
+ signalStrengthFlow(subId).map { it.displayString() }
+ } else {
+ flowOf("0")
+ }
+ }.conflate().flowOn(Dispatchers.Default)
+
+ /** Creates an instance of a cold Flow for [SignalStrength] of given [subId]. */
+ private fun signalStrengthFlow(subId: Int): Flow =
+ context.telephonyCallbackFlow(subId) {
+ object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener {
+ override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
+ trySend(signalStrength)
+ val cellSignalStrengths = signalStrength.cellSignalStrengths
+ Log.d(TAG, "[$subId] onSignalStrengthsChanged: $cellSignalStrengths")
+ }
+ }
+ }
+
+ private fun SignalStrength.displayString() =
+ context.getString(R.string.sim_signal_strength, signalDbm(), signalAsu())
+
+ private companion object {
+ private const val TAG = "SignalStrengthRepo"
+
+
+ private fun SignalStrength.signalDbm(): Int =
+ cellSignalStrengths.firstOrNull { it.dbm != -1 }?.dbm ?: 0
+
+ private fun SignalStrength.signalAsu(): Int =
+ cellSignalStrengths.firstOrNull { it.asuLevel != -1 }?.asuLevel ?: 0
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index e5882dd9759..b5ee1d88108 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -32,10 +32,8 @@ import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
import android.telephony.CellBroadcastIntents;
import android.telephony.CellBroadcastService;
-import android.telephony.CellSignalStrength;
import android.telephony.ICellBroadcastService;
import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -113,7 +111,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
private SubscriptionInfo mSubscriptionInfo;
private TelephonyDisplayInfo mTelephonyDisplayInfo;
- private ServiceState mPreviousServiceState;
private final int mSlotIndex;
private TelephonyManager mTelephonyManager;
@@ -219,15 +216,12 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
// getServiceState() may return null when the subscription is inactive
// or when there was an error communicating with the phone process.
final ServiceState serviceState = getTelephonyManager().getServiceState();
- final SignalStrength signalStrength = getTelephonyManager().getSignalStrength();
updatePhoneNumber();
updateServiceState(serviceState);
- updateSignalStrength(signalStrength);
updateNetworkType();
updateRoamingStatus(serviceState);
updateIccidNumber();
- updateImsRegistrationState();
}
/**
@@ -257,7 +251,7 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
.registerTelephonyCallback(mContext.getMainExecutor(), mTelephonyCallback);
mSubscriptionManager.addOnSubscriptionsChangedListener(
mContext.getMainExecutor(), mOnSubscriptionsChangedListener);
- collectImsRegistered(owner);
+ collectSimStatusDialogInfo(owner);
if (mShowLatestAreaInfo) {
updateAreaInfoText();
@@ -420,12 +414,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
private void updateServiceState(ServiceState serviceState) {
final int state = Utils.getCombinedServiceState(serviceState);
- if (!Utils.isInService(serviceState)) {
- resetSignalStrength();
- } else if (!Utils.isInService(mPreviousServiceState)) {
- // If ServiceState changed from out of service -> in service, update signal strength.
- updateSignalStrength(getTelephonyManager().getSignalStrength());
- }
String serviceStateValue;
@@ -450,49 +438,11 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
mDialog.setText(SERVICE_STATE_VALUE_ID, serviceStateValue);
}
- private void updateSignalStrength(SignalStrength signalStrength) {
- if (signalStrength == null) {
- return;
- }
- // by default we show the signal strength
- boolean showSignalStrength = true;
- if (mSubscriptionInfo != null) {
- final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
- final PersistableBundle carrierConfig =
- mCarrierConfigManager.getConfigForSubId(subscriptionId);
- if (carrierConfig != null) {
- showSignalStrength = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL);
- }
- }
- if (!showSignalStrength) {
- mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID);
- mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID);
- return;
- }
-
- ServiceState serviceState = getTelephonyManager().getServiceState();
- if (!Utils.isInService(serviceState)) {
- return;
- }
-
- int signalDbm = getDbm(signalStrength);
- int signalAsu = getAsuLevel(signalStrength);
-
- if (signalDbm == -1) {
- signalDbm = 0;
- }
-
- if (signalAsu == -1) {
- signalAsu = 0;
- }
-
- mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, mRes.getString(R.string.sim_signal_strength,
- signalDbm, signalAsu));
- }
-
- private void resetSignalStrength() {
- mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, "0");
+ private void updateSignalStrength(@Nullable String signalStrength) {
+ boolean isVisible = signalStrength != null;
+ mDialog.setSettingVisibility(SIGNAL_STRENGTH_LABEL_ID, isVisible);
+ mDialog.setSettingVisibility(SIGNAL_STRENGTH_VALUE_ID, isVisible);
+ mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, signalStrength);
}
private void updateNetworkType() {
@@ -581,39 +531,21 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
}
}
- private boolean isImsRegistrationStateShowUp() {
- if (mSubscriptionInfo == null) {
- return false;
- }
- final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
- final PersistableBundle carrierConfig =
- mCarrierConfigManager.getConfigForSubId(subscriptionId);
- return carrierConfig == null ? false :
- carrierConfig.getBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
+ private void updateImsRegistrationState(@Nullable Boolean imsRegistered) {
+ boolean isVisible = imsRegistered != null;
+ mDialog.setSettingVisibility(IMS_REGISTRATION_STATE_LABEL_ID, isVisible);
+ mDialog.setSettingVisibility(IMS_REGISTRATION_STATE_VALUE_ID, isVisible);
+ int stringId = Boolean.TRUE.equals(imsRegistered)
+ ? com.android.settingslib.R.string.ims_reg_status_registered
+ : com.android.settingslib.R.string.ims_reg_status_not_registered;
+ mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(stringId));
}
- private void updateImsRegistrationState() {
- if (isImsRegistrationStateShowUp()) {
- return;
- }
- mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_LABEL_ID);
- mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_VALUE_ID);
- }
-
- private void collectImsRegistered(@NonNull LifecycleOwner owner) {
- if (!isImsRegistrationStateShowUp()) {
- return;
- }
- new ImsRegistrationStateController(mContext).collectImsRegistered(
- owner, mSlotIndex, (Boolean imsRegistered) -> {
- if (imsRegistered) {
- mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(
- com.android.settingslib.R.string.ims_reg_status_registered));
- } else {
- mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(
- com.android.settingslib.R.string.ims_reg_status_not_registered));
- }
+ private void collectSimStatusDialogInfo(@NonNull LifecycleOwner owner) {
+ new SimStatusDialogRepository(mContext).collectSimStatusDialogInfo(
+ owner, mSlotIndex, (simStatusDialogInfo) -> {
+ updateSignalStrength(simStatusDialogInfo.getSignalStrength());
+ updateImsRegistrationState(simStatusDialogInfo.getImsRegistered());
return Unit.INSTANCE;
}
);
@@ -623,44 +555,9 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
return SubscriptionManager.from(mContext).getActiveSubscriptionInfoForSimSlotIndex(slotId);
}
- private int getDbm(SignalStrength signalStrength) {
- List cellSignalStrengthList = signalStrength.getCellSignalStrengths();
- int dbm = -1;
- if (cellSignalStrengthList == null) {
- return dbm;
- }
-
- for (CellSignalStrength cell : cellSignalStrengthList) {
- if (cell.getDbm() != -1) {
- dbm = cell.getDbm();
- break;
- }
- }
-
- return dbm;
- }
-
- private int getAsuLevel(SignalStrength signalStrength) {
- List cellSignalStrengthList = signalStrength.getCellSignalStrengths();
- int asu = -1;
- if (cellSignalStrengthList == null) {
- return asu;
- }
-
- for (CellSignalStrength cell : cellSignalStrengthList) {
- if (cell.getAsuLevel() != -1) {
- asu = cell.getAsuLevel();
- break;
- }
- }
-
- return asu;
- }
-
@VisibleForTesting
class SimStatusDialogTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
- TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.DisplayInfoListener {
@Override
@@ -669,17 +566,11 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
updateNetworkType();
}
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- updateSignalStrength(signalStrength);
- }
-
@Override
public void onServiceStateChanged(ServiceState serviceState) {
updateNetworkProvider();
updateServiceState(serviceState);
updateRoamingStatus(serviceState);
- mPreviousServiceState = serviceState;
}
@Override
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
index f212eea0d2e..c51417cfcb6 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
@@ -26,6 +26,7 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -91,6 +92,13 @@ public class SimStatusDialogFragment extends InstrumentedDialogFragment {
super.onDestroy();
}
+ public void setSettingVisibility(int viewId, boolean isVisible) {
+ final View view = mRootView.findViewById(viewId);
+ if (view != null) {
+ view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+ }
+ }
+
public void removeSettingFromScreen(int viewId) {
final View view = mRootView.findViewById(viewId);
if (view != null) {
@@ -106,7 +114,7 @@ public class SimStatusDialogFragment extends InstrumentedDialogFragment {
SimStatusDialogController.PHONE_NUMBER_VALUE_ID)
.sorted().toArray();
- public void setText(int viewId, CharSequence text) {
+ public void setText(int viewId, @Nullable CharSequence text) {
if (!isAdded()) {
Log.d(TAG, "Fragment not attached yet.");
return;
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt
new file mode 100644
index 00000000000..5ed6993cc98
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.deviceinfo.simstatus
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.settings.network.telephony.SimSlotRepository
+import com.android.settings.network.telephony.ims.ImsMmTelRepository
+import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
+import com.android.settings.network.telephony.safeGetConfig
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class SimStatusDialogRepository @JvmOverloads constructor(
+ private val context: Context,
+ private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
+ private val signalStrengthRepository: SignalStrengthRepository =
+ SignalStrengthRepository(context),
+ private val imsMmTelRepositoryFactory: (subId: Int) -> ImsMmTelRepository = { subId ->
+ ImsMmTelRepositoryImpl(context, subId)
+ },
+) {
+ private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
+
+ data class SimStatusDialogInfo(
+ val signalStrength: String? = null,
+ val imsRegistered: Boolean? = null,
+ )
+
+ private data class SimStatusDialogVisibility(
+ val signalStrengthShowUp: Boolean,
+ val imsRegisteredShowUp: Boolean,
+ )
+
+ fun collectSimStatusDialogInfo(
+ lifecycleOwner: LifecycleOwner,
+ simSlotIndex: Int,
+ action: (info: SimStatusDialogInfo) -> Unit,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ simStatusDialogInfoBySlotFlow(simSlotIndex).collect(action)
+ }
+ }
+ }
+
+ private fun simStatusDialogInfoBySlotFlow(simSlotIndex: Int): Flow =
+ simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
+ .flatMapLatest { subId ->
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ simStatusDialogInfoFlow(subId)
+ } else {
+ flowOf(SimStatusDialogInfo())
+ }
+ }
+ .conflate()
+ .flowOn(Dispatchers.Default)
+
+ private fun simStatusDialogInfoFlow(subId: Int): Flow =
+ showUpFlow(subId).flatMapLatest { visibility ->
+ combine(
+ if (visibility.signalStrengthShowUp) {
+ signalStrengthRepository.signalStrengthDisplayFlow(subId)
+ } else flowOf(null),
+ if (visibility.imsRegisteredShowUp) {
+ imsMmTelRepositoryFactory(subId).imsRegisteredFlow()
+ } else flowOf(null),
+ ) { signalStrength, imsRegistered ->
+ SimStatusDialogInfo(signalStrength = signalStrength, imsRegistered = imsRegistered)
+ }
+ }
+
+ private fun showUpFlow(subId: Int) = flow {
+ val config = carrierConfigManager.safeGetConfig(
+ keys = listOf(
+ CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
+ CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
+ ),
+ subId = subId,
+ )
+ val visibility = SimStatusDialogVisibility(
+ signalStrengthShowUp = config.getBoolean(
+ CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
+ true, // by default we show the signal strength in sim status
+ ),
+ imsRegisteredShowUp = config.getBoolean(
+ CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL
+ ),
+ )
+ emit(visibility)
+ }
+}
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index 66428611a5b..99441b0cc50 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -33,7 +33,9 @@ import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import androidx.window.embedding.ActivityEmbeddingController;
@@ -210,6 +212,9 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
+ if (Flags.homepageRevamp()) {
+ return;
+ }
int tintColor = Utils.getHomepageIconColor(getContext());
iteratePreferences(preference -> {
Drawable icon = preference.getIcon();
@@ -364,13 +369,17 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
}
job.init();
- int count = screen.getPreferenceCount();
+ iteratePreferences(screen, job);
+ }
+
+ private void iteratePreferences(PreferenceGroup group, PreferenceJob job) {
+ int count = group.getPreferenceCount();
for (int i = 0; i < count; i++) {
- Preference preference = screen.getPreference(i);
- if (preference == null) {
- break;
- }
+ Preference preference = group.getPreference(i);
job.doForEach(preference);
+ if (preference instanceof PreferenceCategory) {
+ iteratePreferences((PreferenceCategory) preference, job);
+ }
}
}
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
index 481dc205f70..3a210493785 100644
--- a/src/com/android/settings/network/SimOnboardingActivity.kt
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -401,6 +401,11 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
Log.d(TAG, "startSimSwitching:")
var targetSubInfo = onboardingService.targetSubInfo
+ if(onboardingService.doesTargetSimActive) {
+ Log.d(TAG, "target subInfo is already active")
+ callbackListener(CallbackType.CALLBACK_SETUP_NAME)
+ return
+ }
targetSubInfo?.let {
var removedSubInfo = onboardingService.getRemovedSim()
if (targetSubInfo.isEmbedded) {
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index b7df7548f69..f6c6065e40d 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -16,6 +16,9 @@
package com.android.settings.network
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX
+
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
@@ -40,26 +43,26 @@ class SimOnboardingService {
var subscriptionManager:SubscriptionManager? = null
var telephonyManager:TelephonyManager? = null
- var targetSubId: Int = INVALID
+ var targetSubId: Int = INVALID_SUBSCRIPTION_ID
var targetSubInfo: SubscriptionInfo? = null
var availableSubInfoList: List = listOf()
var activeSubInfoList: List = listOf()
var slotInfoList: List = listOf()
var uiccCardInfoList: List = listOf()
- var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
- var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
- var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimCalls: Int = INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimTexts: Int = INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimMobileData: Int = INVALID_SUBSCRIPTION_ID
val targetPrimarySimAutoDataSwitch = MutableStateFlow(false)
- var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetNonDds: Int = INVALID_SUBSCRIPTION_ID
get() {
- if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if(targetPrimarySimMobileData == INVALID_SUBSCRIPTION_ID) {
Log.w(TAG, "No DDS")
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ return INVALID_SUBSCRIPTION_ID
}
return userSelectedSubInfoList
.filter { info -> info.subscriptionId != targetPrimarySimMobileData }
.map { it.subscriptionId }
- .firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ .firstOrNull() ?: INVALID_SUBSCRIPTION_ID
}
var callback: (CallbackType) -> Unit = {}
@@ -84,6 +87,11 @@ class SimOnboardingService {
activeSubInfoList.stream().anyMatch { it.isEmbedded }
return false
}
+ var doesTargetSimActive = false
+ get() {
+ return targetSubInfo?.getSimSlotIndex() ?: INVALID_SIM_SLOT_INDEX >= 0
+ }
+
var doesTargetSimHaveEsimOperation = false
get() {
return targetSubInfo?.isEmbedded ?: false
@@ -95,7 +103,7 @@ class SimOnboardingService {
}
var getActiveModemCount = 0
get() {
- return telephonyManager?.getActiveModemCount() ?: 0
+ return (telephonyManager?.getActiveModemCount() ?: 0)
}
var renameMutableMap : MutableMap = mutableMapOf()
@@ -103,16 +111,18 @@ class SimOnboardingService {
var isSimSelectionFinished = false
get() {
- return getActiveModemCount != 0 && userSelectedSubInfoList.size == getActiveModemCount
+ val activeModem = getActiveModemCount
+ return activeModem != 0 && userSelectedSubInfoList.size == activeModem
}
var isAllOfSlotAssigned = false
get() {
- if(getActiveModemCount == 0){
+ val activeModem = getActiveModemCount
+ if(activeModem == 0){
Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0")
return true
}
- return getActiveModemCount != 0 && activeSubInfoList.size == getActiveModemCount
+ return getActiveModemCount != 0 && activeSubInfoList.size == activeModem
}
var isMultiSimEnabled = false
get() {
@@ -129,7 +139,7 @@ class SimOnboardingService {
}
fun isValid(): Boolean {
- return targetSubId != INVALID
+ return targetSubId != INVALID_SUBSCRIPTION_ID
&& targetSubInfo != null
&& activeSubInfoList.isNotEmpty()
&& slotInfoList.isNotEmpty()
@@ -156,14 +166,15 @@ class SimOnboardingService {
fun initData(inputTargetSubId: Int,
context: Context,
callback: (CallbackType) -> Unit) {
+ clear()
this.callback = callback
targetSubId = inputTargetSubId
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
telephonyManager = context.getSystemService(TelephonyManager::class.java)
+ activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
Log.d(
TAG, "startInit: targetSubId:$targetSubId, activeSubInfoList: $activeSubInfoList"
)
- activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
ThreadUtils.postOnBackgroundThread {
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
@@ -242,14 +253,19 @@ class SimOnboardingService {
fun addCurrentItemForSelectedSim() {
if (userSelectedSubInfoList.size < getActiveModemCount) {
- userSelectedSubInfoList.addAll(activeSubInfoList)
- Log.d(TAG, "addCurrentItemForSelectedSim: userSelectedSubInfoList:" +
- ", $userSelectedSubInfoList")
+ userSelectedSubInfoList.addAll(
+ activeSubInfoList.filter { !userSelectedSubInfoList.contains(it) }
+ )
+ Log.d(TAG,
+ "addCurrentItemForSelectedSim: userSelectedSubInfoList: $userSelectedSubInfoList"
+ )
}
}
fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
- userSelectedSubInfoList.add(selectedSubInfo)
+ if (!userSelectedSubInfoList.contains(selectedSubInfo)) {
+ userSelectedSubInfoList.add(selectedSubInfo)
+ }
}
fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
@@ -370,7 +386,6 @@ class SimOnboardingService {
companion object{
private const val TAG = "SimOnboardingService"
- private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
const val NUM_OF_SIMS_FOR_DSDS = 2
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
index b4211854c0e..05b4c07c53e 100644
--- a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
+++ b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
@@ -36,4 +36,7 @@ fun CarrierConfigManager.safeGetConfig(
// Settings should not assume Carrier config loader (and any other system services as well) are
// always available. If not available, use default value instead.
persistableBundleOf()
+} catch (e: RuntimeException) {
+ // The reason is same with above.
+ persistableBundleOf()
}
diff --git a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
index 587f6409dda..aebc4eb5ec6 100644
--- a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
@@ -41,7 +41,7 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon
@Nullable
protected ZenModesBackend mBackend;
- @Nullable // only until updateZenMode() is called
+ @Nullable // only until setZenMode() is called
private ZenMode mZenMode;
@NonNull
@@ -65,7 +65,22 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon
@Override
public boolean isAvailable() {
- return Flags.modesUi();
+ if (mZenMode != null) {
+ return Flags.modesUi() && isAvailable(mZenMode);
+ } else {
+ return Flags.modesUi();
+ }
+ }
+
+ public boolean isAvailable(@NonNull ZenMode zenMode) {
+ return true;
+ }
+
+ // Called by parent Fragment onAttach, for any methods (such as isAvailable()) that need
+ // zen mode info before onStart. Most callers should use updateZenMode instead, which will
+ // do any further necessary propagation.
+ protected final void setZenMode(@NonNull ZenMode zenMode) {
+ mZenMode = zenMode;
}
// Called by the parent Fragment onStart, which means it will happen before resume.
diff --git a/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java
new file mode 100644
index 00000000000..ccd35eca1d5
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import android.app.Application;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.text.BidiFormatter;
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.notification.app.AppChannelsBypassingDndSettings;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.AppPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * When clicked, populates the PreferenceScreen with apps that aren't already bypassing DND. The
+ * user can click on these Preferences to allow notification channels from the app to bypass DND.
+ */
+public class ZenModeAddBypassingAppsPreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin {
+
+ public static final String KEY_NO_APPS = "add_none";
+ private static final String KEY = "zen_mode_non_bypassing_apps_list";
+ private static final String KEY_ADD = "zen_mode_bypassing_apps_add";
+ @Nullable private final NotificationBackend mNotificationBackend;
+
+ @Nullable @VisibleForTesting ApplicationsState mApplicationsState;
+ @VisibleForTesting PreferenceScreen mPreferenceScreen;
+ @VisibleForTesting PreferenceCategory mPreferenceCategory;
+ @VisibleForTesting Context mPrefContext;
+
+ private Preference mAddPreference;
+ private ApplicationsState.Session mAppSession;
+ @Nullable private Fragment mHostFragment;
+
+ public ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable Application app,
+ @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) {
+ this(context, app == null ? null : ApplicationsState.getInstance(app), host,
+ notificationBackend);
+ }
+
+ private ZenModeAddBypassingAppsPreferenceController(Context context,
+ @Nullable ApplicationsState appState, @Nullable Fragment host,
+ @Nullable NotificationBackend notificationBackend) {
+ super(context);
+ mNotificationBackend = notificationBackend;
+ mApplicationsState = appState;
+ mHostFragment = host;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceScreen = screen;
+ mAddPreference = screen.findPreference(KEY_ADD);
+ mAddPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ mAddPreference.setVisible(false);
+ if (mApplicationsState != null && mHostFragment != null) {
+ mAppSession = mApplicationsState.newSession(mAppSessionCallbacks,
+ mHostFragment.getLifecycle());
+ }
+ return true;
+ }
+ });
+ mPrefContext = screen.getContext();
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ /**
+ * Call this method to trigger the app list to refresh.
+ */
+ public void updateAppList() {
+ if (mAppSession == null) {
+ return;
+ }
+
+ ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ ? ApplicationsState.FILTER_ENABLED_NOT_QUIET
+ : ApplicationsState.FILTER_ALL_ENABLED;
+ mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR);
+ }
+
+ // Set the icon for the given preference to the entry icon from cache if available, or look
+ // it up.
+ private void updateIcon(Preference pref, ApplicationsState.AppEntry entry) {
+ synchronized (entry) {
+ final Drawable cachedIcon = AppUtils.getIconFromCache(entry);
+ if (cachedIcon != null && entry.mounted) {
+ pref.setIcon(cachedIcon);
+ } else {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Drawable icon = AppUtils.getIcon(mPrefContext, entry);
+ if (icon != null) {
+ ThreadUtils.postOnMainThread(() -> pref.setIcon(icon));
+ }
+ });
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void updateAppList(List apps) {
+ if (apps == null) {
+ return;
+ }
+
+ if (mPreferenceCategory == null) {
+ mPreferenceCategory = new PreferenceCategory(mPrefContext);
+ mPreferenceCategory.setTitle(R.string.zen_mode_bypassing_apps_add_header);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ }
+
+ boolean doAnyAppsPassCriteria = false;
+ for (ApplicationsState.AppEntry app : apps) {
+ String pkg = app.info.packageName;
+ final String key = getKey(pkg, app.info.uid);
+ final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
+ final int appChannelsBypassingDnd = mNotificationBackend
+ .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
+ if (appChannelsBypassingDnd == 0 && appChannels > 0) {
+ doAnyAppsPassCriteria = true;
+ }
+
+ Preference pref = mPreferenceCategory.findPreference(key);
+
+ if (pref == null) {
+ if (appChannelsBypassingDnd == 0 && appChannels > 0) {
+ // does not exist but should
+ pref = new AppPreference(mPrefContext);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(preference -> {
+ Bundle args = new Bundle();
+ args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.info.packageName);
+ args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.info.uid);
+ new SubSettingLauncher(mContext)
+ .setDestination(AppChannelsBypassingDndSettings.class.getName())
+ .setArguments(args)
+ .setResultListener(mHostFragment, 0)
+ .setUserHandle(new UserHandle(UserHandle.getUserId(app.info.uid)))
+ .setSourceMetricsCategory(
+ SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
+ .launch();
+ return true;
+ });
+ pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
+ updateIcon(pref, app);
+ mPreferenceCategory.addPreference(pref);
+ }
+ } else if (appChannelsBypassingDnd != 0 || appChannels == 0) {
+ // exists but shouldn't anymore
+ mPreferenceCategory.removePreference(pref);
+ }
+ }
+
+ Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS);
+ if (!doAnyAppsPassCriteria) {
+ if (pref == null) {
+ pref = new Preference(mPrefContext);
+ pref.setKey(KEY_NO_APPS);
+ pref.setTitle(R.string.zen_mode_bypassing_apps_none);
+ }
+ mPreferenceCategory.addPreference(pref);
+ } else if (pref != null) {
+ mPreferenceCategory.removePreference(pref);
+ }
+ }
+
+ static String getKey(String pkg, int uid) {
+ return "add|" + pkg + "|" + uid;
+ }
+
+ private final ApplicationsState.Callbacks mAppSessionCallbacks =
+ new ApplicationsState.Callbacks() {
+
+ @Override
+ public void onRunningStateChanged(boolean running) {
+
+ }
+
+ @Override
+ public void onPackageListChanged() {
+
+ }
+
+ @Override
+ public void onRebuildComplete(ArrayList apps) {
+ updateAppList(apps);
+ }
+
+ @Override
+ public void onPackageIconChanged() {
+ updateAppList();
+ }
+
+ @Override
+ public void onPackageSizeChanged(String packageName) {
+
+ }
+
+ @Override
+ public void onAllSizesComputed() { }
+
+ @Override
+ public void onLauncherInfoChanged() {
+
+ }
+
+ @Override
+ public void onLoadEntriesCompleted() {
+ updateAppList();
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java
new file mode 100644
index 00000000000..922ac5ecf5b
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import android.app.Application;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.text.BidiFormatter;
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.notification.app.AppChannelsBypassingDndSettings;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.AppPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adds a preference to the PreferenceScreen for each notification channel that can bypass DND.
+ */
+public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin {
+ public static final String KEY_NO_APPS = "all_none";
+ private static final String KEY = "zen_mode_bypassing_apps_list";
+
+ @Nullable private final NotificationBackend mNotificationBackend;
+
+ @Nullable @VisibleForTesting ApplicationsState mApplicationsState;
+ @VisibleForTesting PreferenceCategory mPreferenceCategory;
+ @VisibleForTesting Context mPrefContext;
+
+ private ApplicationsState.Session mAppSession;
+ @Nullable private Fragment mHostFragment;
+
+ public ZenModeAllBypassingAppsPreferenceController(Context context, @Nullable Application app,
+ @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) {
+ this(context, app == null ? null : ApplicationsState.getInstance(app), host,
+ notificationBackend);
+ }
+
+ private ZenModeAllBypassingAppsPreferenceController(Context context,
+ @Nullable ApplicationsState appState, @Nullable Fragment host,
+ @Nullable NotificationBackend notificationBackend) {
+ super(context);
+ mNotificationBackend = notificationBackend;
+ mApplicationsState = appState;
+ mHostFragment = host;
+
+ if (mApplicationsState != null && host != null) {
+ mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceCategory = screen.findPreference(KEY);
+ mPrefContext = screen.getContext();
+ updateAppList();
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ /**
+ * Call this method to trigger the app list to refresh.
+ */
+ public void updateAppList() {
+ if (mAppSession == null) {
+ return;
+ }
+
+ ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ ? ApplicationsState.FILTER_ENABLED_NOT_QUIET
+ : ApplicationsState.FILTER_ALL_ENABLED;
+ mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR);
+ }
+
+ // Set the icon for the given preference to the entry icon from cache if available, or look
+ // it up.
+ private void updateIcon(Preference pref, ApplicationsState.AppEntry entry) {
+ synchronized (entry) {
+ final Drawable cachedIcon = AppUtils.getIconFromCache(entry);
+ if (cachedIcon != null && entry.mounted) {
+ pref.setIcon(cachedIcon);
+ } else {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Drawable icon = AppUtils.getIcon(mPrefContext, entry);
+ if (icon != null) {
+ ThreadUtils.postOnMainThread(() -> pref.setIcon(icon));
+ }
+ });
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void updateAppList(List apps) {
+ if (mPreferenceCategory == null || apps == null) {
+ return;
+ }
+
+ boolean doAnyAppsPassCriteria = false;
+ for (ApplicationsState.AppEntry app : apps) {
+ String pkg = app.info.packageName;
+ final String key = getKey(pkg, app.info.uid);
+ final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
+ final int appChannelsBypassingDnd = mNotificationBackend
+ .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
+ if (appChannelsBypassingDnd > 0) {
+ doAnyAppsPassCriteria = true;
+ }
+
+ Preference pref = mPreferenceCategory.findPreference(key);
+ if (pref == null) {
+ if (appChannelsBypassingDnd > 0) {
+ // does not exist but should
+ pref = new AppPreference(mPrefContext);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(preference -> {
+ Bundle args = new Bundle();
+ args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.info.packageName);
+ args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.info.uid);
+ new SubSettingLauncher(mContext)
+ .setDestination(AppChannelsBypassingDndSettings.class.getName())
+ .setArguments(args)
+ .setUserHandle(UserHandle.getUserHandleForUid(app.info.uid))
+ .setResultListener(mHostFragment, 0)
+ .setSourceMetricsCategory(
+ SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
+ .launch();
+ return true;
+ });
+ pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
+ updateIcon(pref, app);
+ if (appChannels > appChannelsBypassingDnd) {
+ pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some);
+ } else {
+ pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all);
+ }
+ mPreferenceCategory.addPreference(pref);
+ }
+ } else if (appChannelsBypassingDnd == 0) {
+ // exists but shouldn't anymore
+ mPreferenceCategory.removePreference(pref);
+ }
+ }
+
+ Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS);
+ if (!doAnyAppsPassCriteria) {
+ if (pref == null) {
+ pref = new Preference(mPrefContext);
+ pref.setKey(KEY_NO_APPS);
+ pref.setTitle(R.string.zen_mode_bypassing_apps_none);
+ }
+ mPreferenceCategory.addPreference(pref);
+ } else if (pref != null) {
+ mPreferenceCategory.removePreference(pref);
+ }
+ }
+
+ /**
+ * Create a unique key to idenfity an AppPreference
+ */
+ static String getKey(String pkg, int uid) {
+ return "all|" + pkg + "|" + uid;
+ }
+
+ private final ApplicationsState.Callbacks mAppSessionCallbacks =
+ new ApplicationsState.Callbacks() {
+
+ @Override
+ public void onRunningStateChanged(boolean running) {
+ }
+
+ @Override
+ public void onPackageListChanged() {
+ }
+
+ @Override
+ public void onRebuildComplete(ArrayList apps) {
+ updateAppList(apps);
+ }
+
+ @Override
+ public void onPackageIconChanged() {
+ }
+
+ @Override
+ public void onPackageSizeChanged(String packageName) {
+ }
+
+ @Override
+ public void onAllSizesComputed() { }
+
+ @Override
+ public void onLauncherInfoChanged() {
+ }
+
+ @Override
+ public void onLoadEntriesCompleted() {
+ updateAppList();
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java
new file mode 100644
index 00000000000..73329a242fd
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.modes;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mode > Apps
+ */
+public class ZenModeAppsFragment extends ZenModeFragmentBase {
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List controllers = new ArrayList<>();
+ controllers.add(new ZenModeAppsPreferenceController(
+ context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend));
+ controllers.add(new ZenModeAppsPreferenceController(
+ context, ZenModeAppsPreferenceController.KEY_NONE, mBackend));
+ // TODO: b/308819928 - The manual DND mode cannot have the ALL type;
+ // unify the controllers into one and only create a preference if isManualDnd is false.
+ controllers.add(new ZenModeAppsPreferenceController(
+ context, ZenModeAppsPreferenceController.KEY_ALL, mBackend));
+ return controllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.zen_mode_apps_settings;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: b/332937635 - make this the correct metrics category
+ return SettingsEnums.NOTIFICATION_ZEN_MODE_PRIORITY;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
new file mode 100644
index 00000000000..42b58b1346e
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.settings.core.SubSettingLauncher;
+
+/**
+ * Preference with a link and summary about what apps can break through the mode
+ */
+public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {
+
+ private final ZenModeSummaryHelper mSummaryHelper;
+
+ public ZenModeAppsLinkPreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ super(context, key, backend);
+ mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
+ }
+
+ @Override
+ public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ Bundle bundle = new Bundle();
+ bundle.putString(MODE_ID, zenMode.getId());
+ // TODO(b/332937635): Update metrics category
+ preference.setIntent(new SubSettingLauncher(mContext)
+ .setDestination(ZenModeAppsFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+ preference.setSummary(mSummaryHelper.getAppsSummary(zenMode));
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java
new file mode 100644
index 00000000000..704bce04504
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.service.notification.ZenPolicy;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+public class ZenModeAppsPreferenceController extends
+ AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
+
+ static final String KEY_PRIORITY = "zen_mode_apps_priority";
+ static final String KEY_NONE = "zen_mode_apps_none";
+ static final String KEY_ALL = "zen_mode_apps_all";
+
+ String mModeId;
+
+
+ public ZenModeAppsPreferenceController(@NonNull Context context,
+ @NonNull String key, @Nullable ZenModesBackend backend) {
+ super(context, key, backend);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ SelectorWithWidgetPreference pref = screen.findPreference(getPreferenceKey());
+ if (pref != null) {
+ pref.setOnClickListener(mSelectorClickListener);
+
+ // Adds the widget to only the priority category.
+ if (getPreferenceKey().equals(KEY_PRIORITY)) {
+ pref.setExtraWidgetOnClickListener(p -> {
+ launchPrioritySettings();
+ });
+ }
+ }
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ mModeId = zenMode.getId();
+ TwoStatePreference pref = (TwoStatePreference) preference;
+ switch (getPreferenceKey()) {
+ case KEY_PRIORITY:
+ boolean policy_priority = zenMode.getPolicy().getAllowedChannels()
+ == ZenPolicy.CHANNEL_POLICY_PRIORITY;
+ pref.setChecked(policy_priority);
+ break;
+ case KEY_NONE:
+ boolean policy_none = zenMode.getPolicy().getAllowedChannels()
+ == ZenPolicy.CHANNEL_POLICY_NONE;
+ pref.setChecked(policy_none);
+ break;
+ case KEY_ALL:
+ // A UI-only setting; the underlying policy never actually has this value,
+ // but ZenMode acts as though it does for the sake of UI consistency.
+ boolean policy_all = zenMode.getPolicy().getAllowedChannels()
+ == ZenMode.CHANNEL_POLICY_ALL;
+ pref.setChecked(policy_all);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
+ switch (getPreferenceKey()) {
+ case KEY_PRIORITY:
+ return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY));
+ case KEY_NONE:
+ return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE));
+ case KEY_ALL:
+ return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL));
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
+ new SelectorWithWidgetPreference.OnClickListener() {
+ @Override
+ public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
+ onPreferenceChange(preference, true);
+ }
+ };
+
+ private void launchPrioritySettings() {
+ Bundle bundle = new Bundle();
+ if (mModeId != null) {
+ bundle.putString(MODE_ID, mModeId);
+ }
+ // TODO(b/332937635): Update metrics category
+ new SubSettingLauncher(mContext)
+ .setDestination(ZenModeSelectBypassingAppsFragment.class.getName())
+ .setSourceMetricsCategory(SettingsEnums.SETTINGS_ZEN_NOTIFICATIONS)
+ .setArguments(bundle)
+ .launch();
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java
new file mode 100644
index 00000000000..1846dfc2efe
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.modes;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.widget.Button;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settingslib.widget.LayoutPreference;
+
+public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController {
+
+ private Button mZenButton;
+
+ public ZenModeButtonPreferenceController(Context context, String key, ZenModesBackend backend) {
+ super(context, key, backend);
+ }
+
+ @Override
+ public boolean isAvailable(ZenMode zenMode) {
+ return zenMode.getRule().isManualInvocationAllowed() && zenMode.getRule().isEnabled();
+ }
+
+ @Override
+ public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ if (mZenButton == null) {
+ mZenButton = ((LayoutPreference) preference).findViewById(R.id.activate_mode);
+ }
+ mZenButton.setOnClickListener(v -> {
+ if (zenMode.isActive()) {
+ mBackend.deactivateMode(zenMode);
+ } else {
+ mBackend.activateMode(zenMode, null);
+ }
+ });
+ if (zenMode.isActive()) {
+ mZenButton.setText(R.string.zen_mode_button_turn_off);
+ } else {
+ mZenButton.setText(R.string.zen_mode_button_turn_on);
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 7f805eeb669..1f6ae45c462 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -37,8 +37,11 @@ public class ZenModeFragment extends ZenModeFragmentBase {
protected List createPreferenceControllers(Context context) {
List prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
+ prefControllers.add(new ZenModeButtonPreferenceController(context, "activate", mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend));
+ prefControllers.add(new ZenModeAppsLinkPreferenceController(
+ context, "zen_mode_apps", mBackend));
prefControllers.add(new ZenModeOtherLinkPreferenceController(
context, "zen_other_settings", mBackend));
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
diff --git a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
index 71fbc02ed35..ff75afc756b 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
@@ -53,10 +53,25 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
if (!reloadMode(id)) {
Log.d(TAG, "Mode id " + id + " not found");
toastAndFinish();
+ return;
}
} else {
Log.d(TAG, "Mode id required to set mode config settings");
toastAndFinish();
+ return;
+ }
+ if (mZenMode != null) {
+ // Propagate mode info through to controllers.
+ for (List list : getPreferenceControllers()) {
+ try {
+ for (AbstractPreferenceController controller : list) {
+ // mZenMode guaranteed non-null from reloadMode() above
+ ((AbstractZenModePreferenceController) controller).setZenMode(mZenMode);
+ }
+ } catch (ClassCastException e) {
+ // ignore controllers that aren't AbstractZenModePreferenceController
+ }
+ }
}
}
diff --git a/src/com/android/settings/notification/modes/ZenModeHeaderController.java b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
index f791519b178..fc20710ab78 100644
--- a/src/com/android/settings/notification/modes/ZenModeHeaderController.java
+++ b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
@@ -51,6 +51,7 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController {
if (mFragment == null) {
return;
}
+ preference.setSelectable(false);
if (mHeaderController == null) {
final LayoutPreference pref = (LayoutPreference) preference;
diff --git a/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java
new file mode 100644
index 00000000000..8b682b92f1a
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.modes;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.Indexable;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SearchIndexable
+public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase implements
+ Indexable {
+ private static final String TAG = "ZenBypassingApps";
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ final Activity activity = getActivity();
+ final Application app;
+ if (activity != null) {
+ app = activity.getApplication();
+ } else {
+ app = null;
+ }
+ return buildPreferenceControllers(context, app, this, new NotificationBackend());
+ }
+
+ private static List buildPreferenceControllers(Context context,
+ @Nullable Application app, @Nullable Fragment host,
+ @Nullable NotificationBackend notificationBackend) {
+ final List controllers = new ArrayList<>();
+ controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host,
+ notificationBackend));
+ controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host,
+ notificationBackend));
+ return controllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.zen_mode_select_bypassing_apps;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/332937635): Update metrics category
+ return SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APPS;
+ }
+
+ /**
+ * For Search.
+ */
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.zen_mode_select_bypassing_apps) {
+
+ @Override
+ public List createPreferenceControllers(
+ Context context) {
+ return buildPreferenceControllers(context, null, null, null);
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index 41a3d20d0a3..b4075cde656 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -395,4 +395,19 @@ class ZenModeSummaryHelper {
return mContext.getResources().getString(R.string.zen_mode_people_some);
}
}
+
+ /**
+ * Generates a summary to display under the top level "Apps" preference for a mode.
+ */
+ public String getAppsSummary(ZenMode zenMode) {
+ // TODO: b/308819928 - Set summary using priority app list if Selected Apps Chosen.
+ if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) {
+ return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
+ } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
+ return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
+ } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
+ return mContext.getResources().getString(R.string.zen_mode_apps_all_apps);
+ }
+ return "";
+ }
}
diff --git a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
index 595f1d08f29..3f33b026f8d 100644
--- a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
@@ -60,17 +60,15 @@ abstract class ZenModesFragmentBase extends RestrictedDashboardFragment {
mContext = context;
mBackend = ZenModesBackend.getInstance(context);
super.onAttach(context);
+ mSettingsObserver.register();
}
@Override
public void onStart() {
super.onStart();
- updateZenModeState();
- mSettingsObserver.register();
if (isUiRestricted()) {
if (isUiRestrictedByOnlyAdmin()) {
getPreferenceScreen().removeAll();
- return;
} else {
finish();
}
@@ -84,8 +82,8 @@ abstract class ZenModesFragmentBase extends RestrictedDashboardFragment {
}
@Override
- public void onStop() {
- super.onStop();
+ public void onDetach() {
+ super.onDetach();
mSettingsObserver.unregister();
}
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index dc9c2ec98b8..069f910adc0 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -313,7 +313,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
mForceVerifyPath = userProperties.isCredentialShareableWithParent();
if (android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
&& isBiometricAllowed(effectiveUserId, mUserId)) {
- promptInfo.setUseParentProfileForDeviceCredential(true);
+ setBiometricPromptPropertiesForPrivateProfile(promptInfo);
showBiometricPrompt(promptInfo, effectiveUserId);
launchedBiometric = true;
} else {
@@ -344,6 +344,11 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
}
}
+ private static void setBiometricPromptPropertiesForPrivateProfile(PromptInfo promptInfo) {
+ promptInfo.setUseParentProfileForDeviceCredential(true);
+ promptInfo.setConfirmationRequested(false);
+ }
+
private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
boolean isEffectiveUserManagedProfile) {
switch (credentialType) {
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index debec45a159..bdbe993f8fc 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -19,6 +19,7 @@ package com.android.settings.privatespace;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK;
+import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART;
import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK;
import static android.provider.Settings.Secure.SKIP_FIRST_USE_HINTS;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -70,6 +71,10 @@ public class PrivateSpaceMaintainer {
@Settings.Secure.PrivateSpaceAutoLockOption
public static final int PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL =
PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK;
+ /** Value for private space auto lock settings after private space creation. */
+ @Settings.Secure.PrivateSpaceAutoLockOption
+ public static final int PRIVATE_SPACE_CREATE_AUTO_LOCK_VAL =
+ PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART;
/** Default value for the hide private space sensitive notifications on lockscreen. */
public static final int HIDE_PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DISABLED_VAL = 0;
@@ -327,7 +332,7 @@ public class PrivateSpaceMaintainer {
@GuardedBy("this")
private void resetPrivateSpaceSettings() {
setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
- setPrivateSpaceAutoLockSetting(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+ setPrivateSpaceAutoLockSetting(PRIVATE_SPACE_CREATE_AUTO_LOCK_VAL);
setPrivateSpaceSensitiveNotificationsDefaultValue();
}
diff --git a/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java b/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
index 9d04e7900aa..4f2634eb775 100644
--- a/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
+++ b/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
@@ -19,6 +19,8 @@ package com.android.settings.privatespace;
import static android.content.Intent.ACTION_PROFILE_INACCESSIBLE;
import static android.content.Intent.ACTION_PROFILE_UNAVAILABLE;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL;
+
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -101,8 +103,13 @@ public class SetupPreFinishDelayFragment extends InstrumentedFragment {
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
if (savedInstanceState == null) {
- // TODO(b/307729746): Add a test to verify PS is locked after setup completion.
- PrivateSpaceMaintainer.getInstance(getActivity()).lockPrivateSpace();
+ // TODO(b/307729746): Add test to verify PS is locked and auto-lock value is set to
+ // auto-lock on device lock after setup completion.
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(getActivity());
+ privateSpaceMaintainer.setPrivateSpaceAutoLockSetting(
+ PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+ privateSpaceMaintainer.lockPrivateSpace();
}
return rootView;
}
diff --git a/src/com/android/settings/search/BaseSearchIndexProvider.java b/src/com/android/settings/search/BaseSearchIndexProvider.java
index 7fa83554988..cc05270cc0e 100644
--- a/src/com/android/settings/search/BaseSearchIndexProvider.java
+++ b/src/com/android/settings/search/BaseSearchIndexProvider.java
@@ -45,6 +45,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
/**
* A basic SearchIndexProvider that returns no data to index.
@@ -117,11 +118,18 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider {
@Override
@CallSuper
public List getNonIndexableKeys(Context context) {
+ final List nonIndexableKeys = new ArrayList<>();
if (!isPageSearchEnabled(context)) {
// Entire page should be suppressed, mark all keys from this page as non-indexable.
- return getNonIndexableKeysFromXml(context, true /* suppressAllPage */);
+ nonIndexableKeys.addAll(
+ getNonIndexableKeysFromXml(context, true /* suppressAllPage */));
+ nonIndexableKeys.addAll(
+ getRawDataToIndex(context, true /* enabled */)
+ .stream()
+ .map(data -> data.key)
+ .collect(Collectors.toList()));
+ return nonIndexableKeys;
}
- final List nonIndexableKeys = new ArrayList<>();
nonIndexableKeys.addAll(getNonIndexableKeysFromXml(context, false /* suppressAllPage */));
final List controllers = getPreferenceControllers(context);
if (controllers != null && !controllers.isEmpty()) {
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 4dd0defe795..6c8ce6e3bdf 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -28,6 +28,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
@@ -104,9 +105,9 @@ private fun LabelSimPreference(
value = titleSimName,
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
placeholder = {Text(text = originalSimCarrierName)},
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth().testTag("contentInput")
) {
- titleSimName = if (it.isEmpty()) originalSimCarrierName else it
+ titleSimName = if (it.matches(Regex("^\\s*$"))) originalSimCarrierName else it
}
},
)
diff --git a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
index fab9c12e8ac..028b50ddce2 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
@@ -107,7 +107,8 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
composable(route = SimOnboardingScreen.LabelSim.name) {
val nextPage =
if (onboardingService.isMultipleEnabledProfilesSupported
- && onboardingService.isAllOfSlotAssigned) {
+ && onboardingService.isAllOfSlotAssigned
+ && !onboardingService.doesTargetSimActive) {
SimOnboardingScreen.SelectSim.name
} else {
LaunchedEffect(Unit) {
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index 1c6794d027f..7830ab455f2 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -98,6 +98,8 @@ android_robolectric_test {
],
upstream: true,
+
+ strict_mode: false,
}
java_library {
diff --git a/tests/robotests/src/com/android/settings/MainClearConfirmTest.java b/tests/robotests/src/com/android/settings/MainClearConfirmTest.java
index b866c964f03..6d85368d0b8 100644
--- a/tests/robotests/src/com/android/settings/MainClearConfirmTest.java
+++ b/tests/robotests/src/com/android/settings/MainClearConfirmTest.java
@@ -25,6 +25,10 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.FactoryResetProtectionPolicy;
import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.security.Flags;
import android.service.persistentdata.PersistentDataBlockManager;
import android.view.LayoutInflater;
import android.widget.TextView;
@@ -32,6 +36,7 @@ import android.widget.TextView;
import androidx.fragment.app.FragmentActivity;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -48,6 +53,9 @@ import java.util.ArrayList;
})
public class MainClearConfirmTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private FragmentActivity mActivity;
@Mock
@@ -66,6 +74,9 @@ public class MainClearConfirmTest {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.setupActivity(FragmentActivity.class);
mMainClearConfirm = spy(new MainClearConfirm());
+
+ when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
}
@Test
@@ -110,12 +121,29 @@ public class MainClearConfirmTest {
}
@Test
- public void shouldWipePersistentDataBlock_oemUnlockAllowed_shouldReturnFalse() {
+ @DisableFlags(Flags.FLAG_FRP_ENFORCEMENT)
+ public void shouldWipePersistentDataBlock_oemUnlockAllowedAndFlagDiscabled_shouldReturnFalse() {
+ when(mMainClearConfirm.getActivity()).thenReturn(mMockActivity);
+
+ when(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).thenReturn(true);
doReturn(false).when(mMainClearConfirm).isDeviceStillBeingProvisioned();
doReturn(true).when(mMainClearConfirm).isOemUnlockedAllowed();
- assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(
- mPersistentDataBlockManager)).isFalse();
+ assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(mPersistentDataBlockManager))
+ .isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FRP_ENFORCEMENT)
+ public void shouldWipePersistentDataBlock_oemUnlockAllowedAndFlagEnabled_shouldReturnTrue() {
+ when(mMainClearConfirm.getActivity()).thenReturn(mMockActivity);
+
+ when(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).thenReturn(true);
+ doReturn(false).when(mMainClearConfirm).isDeviceStillBeingProvisioned();
+ doReturn(true).when(mMainClearConfirm).isOemUnlockedAllowed();
+
+ assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(mPersistentDataBlockManager))
+ .isTrue();
}
@Test
@@ -124,8 +152,7 @@ public class MainClearConfirmTest {
doReturn(false).when(mMainClearConfirm).isDeviceStillBeingProvisioned();
doReturn(false).when(mMainClearConfirm).isOemUnlockedAllowed();
- when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE))
- .thenReturn(mDevicePolicyManager);
+
when(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).thenReturn(false);
assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(
@@ -144,8 +171,6 @@ public class MainClearConfirmTest {
.setFactoryResetProtectionAccounts(accounts)
.setFactoryResetProtectionEnabled(true)
.build();
- when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE))
- .thenReturn(mDevicePolicyManager);
when(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).thenReturn(true);
when(mDevicePolicyManager.getFactoryResetProtectionPolicy(null)).thenReturn(frp);
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
@@ -161,8 +186,6 @@ public class MainClearConfirmTest {
doReturn(false).when(mMainClearConfirm).isDeviceStillBeingProvisioned();
doReturn(false).when(mMainClearConfirm).isOemUnlockedAllowed();
- when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE))
- .thenReturn(mDevicePolicyManager);
when(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).thenReturn(true);
when(mDevicePolicyManager.getFactoryResetProtectionPolicy(null)).thenReturn(null);
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
index 36c48e3cdd7..aedb9224b2c 100644
--- a/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
@@ -26,15 +26,19 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.flags.Flags;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -42,6 +46,8 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class TopLevelSettingsTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
private TopLevelSettings mSettings;
@@ -58,6 +64,7 @@ public class TopLevelSettingsTest {
}
@Test
+ @DisableFlags(Flags.FLAG_HOMEPAGE_REVAMP)
public void onCreatePreferences_shouldTintPreferenceIcon() {
final Preference preference = new Preference(mContext);
preference.setTitle(R.string.network_dashboard_title);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java
new file mode 100644
index 00000000000..bca1ccf58c6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.modes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModeAddBypassingAppsPreferenceControllerTest {
+
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private ApplicationsState mApplicationState;
+ private ZenModeAddBypassingAppsPreferenceController mController;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mController = new ZenModeAddBypassingAppsPreferenceController(
+ mContext, null, mock(Fragment.class), mBackend);
+ mController.mPreferenceCategory = mPreferenceCategory;
+ mController.mApplicationsState = mApplicationState;
+ mController.mPrefContext = mContext;
+ }
+
+ @Test
+ public void testIsAvailable() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateAppList() {
+ // GIVEN there's an app with bypassing channels, app without any channels, and then an app
+ // with notification channels but none that can bypass DND
+ ApplicationsState.AppEntry appWithBypassingChannels =
+ mock(ApplicationsState.AppEntry.class);
+ appWithBypassingChannels.info = new ApplicationInfo();
+ appWithBypassingChannels.info.packageName = "appWithBypassingChannels";
+ appWithBypassingChannels.info.uid = 0;
+ when(mBackend.getNotificationChannelsBypassingDnd(
+ appWithBypassingChannels.info.packageName,
+ appWithBypassingChannels.info.uid))
+ .thenReturn(new ParceledListSlice<>(
+ Arrays.asList(mock(NotificationChannel.class))));
+ when(mBackend.getChannelCount(
+ appWithBypassingChannels.info.packageName,
+ appWithBypassingChannels.info.uid))
+ .thenReturn(5);
+
+ ApplicationsState.AppEntry appWithoutChannels = mock(ApplicationsState.AppEntry.class);
+ appWithoutChannels.info = new ApplicationInfo();
+ appWithoutChannels.info.packageName = "appWithoutChannels";
+ appWithoutChannels.info.uid = 0;
+ when(mBackend.getChannelCount(
+ appWithoutChannels.info.packageName,
+ appWithoutChannels.info.uid))
+ .thenReturn(0);
+ when(mBackend.getNotificationChannelsBypassingDnd(
+ appWithoutChannels.info.packageName,
+ appWithoutChannels.info.uid))
+ .thenReturn(new ParceledListSlice<>(new ArrayList<>()));
+
+ ApplicationsState.AppEntry appWithChannelsNoneBypassing =
+ mock(ApplicationsState.AppEntry.class);
+ appWithChannelsNoneBypassing.info = new ApplicationInfo();
+ appWithChannelsNoneBypassing.info.packageName = "appWithChannelsNoneBypassing";
+ appWithChannelsNoneBypassing.info.uid = 0;
+ when(mBackend.getChannelCount(
+ appWithChannelsNoneBypassing.info.packageName,
+ appWithChannelsNoneBypassing.info.uid))
+ .thenReturn(5);
+ when(mBackend.getNotificationChannelsBypassingDnd(
+ appWithChannelsNoneBypassing.info.packageName,
+ appWithChannelsNoneBypassing.info.uid))
+ .thenReturn(new ParceledListSlice<>(new ArrayList<>()));
+
+ List appEntries = new ArrayList<>();
+ appEntries.add(appWithBypassingChannels);
+ appEntries.add(appWithoutChannels);
+ appEntries.add(appWithChannelsNoneBypassing);
+
+ // WHEN the controller updates the app list with the app entries
+ mController.updateAppList(appEntries);
+
+ // THEN only the appWithChannelsNoneBypassing makes it to the app list
+ ArgumentCaptor prefCaptor = ArgumentCaptor.forClass(Preference.class);
+ verify(mPreferenceCategory).addPreference(prefCaptor.capture());
+
+ Preference pref = prefCaptor.getValue();
+ assertThat(pref.getKey()).isEqualTo(
+ ZenModeAddBypassingAppsPreferenceController.getKey(
+ appWithChannelsNoneBypassing.info.packageName,
+ appWithChannelsNoneBypassing.info.uid));
+ }
+
+ @Test
+ public void testUpdateAppList_nullApps() {
+ mController.updateAppList(null);
+ verify(mPreferenceCategory, never()).addPreference(any());
+ }
+
+ @Test
+ public void testUpdateAppList_emptyAppList() {
+ // WHEN there are no apps
+ mController.updateAppList(new ArrayList<>());
+
+ // THEN only the appWithChannelsNoneBypassing makes it to the app list
+ ArgumentCaptor prefCaptor = ArgumentCaptor.forClass(Preference.class);
+ verify(mPreferenceCategory).addPreference(prefCaptor.capture());
+
+ Preference pref = prefCaptor.getValue();
+ assertThat(pref.getKey()).isEqualTo(
+ ZenModeAddBypassingAppsPreferenceController.KEY_NO_APPS);
+ }
+
+ // TODO(b/331624810): Add tests to verify updateAppList() when the filter is
+ // ApplicationsState.FILTER_ENABLED_NOT_QUIET
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java
new file mode 100644
index 00000000000..3114a2d01ca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModeAllBypassingAppsPreferenceControllerTest {
+ private ZenModeAllBypassingAppsPreferenceController mController;
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private ApplicationsState mApplicationState;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mController = new ZenModeAllBypassingAppsPreferenceController(
+ mContext, null, mock(Fragment.class), mBackend);
+ mController.mPreferenceCategory = mPreferenceCategory;
+ mController.mApplicationsState = mApplicationState;
+ mController.mPrefContext = mContext;
+ }
+
+ @Test
+ public void testIsAvailable() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateAppList() {
+ // WHEN there's two apps with notification channels that bypass DND
+ ApplicationsState.AppEntry entry1 = mock(ApplicationsState.AppEntry.class);
+ entry1.info = new ApplicationInfo();
+ entry1.info.packageName = "test";
+ entry1.info.uid = 0;
+
+ ApplicationsState.AppEntry entry2 = mock(ApplicationsState.AppEntry.class);
+ entry2.info = new ApplicationInfo();
+ entry2.info.packageName = "test2";
+ entry2.info.uid = 0;
+
+ List appEntries = new ArrayList<>();
+ appEntries.add(entry1);
+ appEntries.add(entry2);
+ List channelsBypassing = new ArrayList<>();
+ channelsBypassing.add(mock(NotificationChannel.class));
+ channelsBypassing.add(mock(NotificationChannel.class));
+ when(mBackend.getNotificationChannelsBypassingDnd(anyString(),
+ anyInt())).thenReturn(new ParceledListSlice<>(channelsBypassing));
+
+ // THEN there's are two preferences
+ mController.updateAppList(appEntries);
+ verify(mPreferenceCategory, times(2)).addPreference(any());
+ }
+
+ @Test
+ public void testUpdateAppList_nullApps() {
+ mController.updateAppList(null);
+ verify(mPreferenceCategory, never()).addPreference(any());
+ }
+
+ @Test
+ public void testUpdateAppList_emptyAppList() {
+ // WHEN there are no apps
+ mController.updateAppList(new ArrayList<>());
+
+ // THEN only the appWithChannelsNoneBypassing makes it to the app list
+ ArgumentCaptor prefCaptor = ArgumentCaptor.forClass(Preference.class);
+ verify(mPreferenceCategory).addPreference(prefCaptor.capture());
+
+ Preference pref = prefCaptor.getValue();
+ assertThat(pref.getKey()).isEqualTo(
+ ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..67e1f9f919a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+
+import androidx.preference.Preference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public final class ZenModeAppsLinkPreferenceControllerTest {
+
+ private ZenModeAppsLinkPreferenceController mController;
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new ZenModeAppsLinkPreferenceController(
+ mContext, "controller_key", mBackend);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testHasSummary() {
+ Preference pref = mock(Preference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build(), true);
+ mController.updateZenMode(pref, zenMode);
+ verify(pref).setSummary(any());
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java
new file mode 100644
index 00000000000..750453dabe6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
+import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_ALL;
+import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_NONE;
+import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public final class ZenModeAppsPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+ private ZenModeAppsPreferenceController mPriorityController;
+ private ZenModeAppsPreferenceController mAllController;
+ private ZenModeAppsPreferenceController mNoneController;
+
+ private SelectorWithWidgetPreference mPriorityPref;
+ private SelectorWithWidgetPreference mAllPref;
+ private SelectorWithWidgetPreference mNonePref;
+ private PreferenceCategory mPrefCategory;
+ private PreferenceScreen mPreferenceScreen;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend);
+ mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend);
+ mAllController = new ZenModeAppsPreferenceController(mContext, KEY_ALL, mBackend);
+
+ mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController);
+ mAllPref = makePreference(KEY_ALL, mAllController);
+ mNonePref = makePreference(KEY_NONE, mNoneController);
+
+ mPrefCategory = new PreferenceCategory(mContext);
+ mPrefCategory.setKey("zen_mode_apps_category");
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+
+ mPreferenceScreen.addPreference(mPrefCategory);
+ mPrefCategory.addPreference(mPriorityPref);
+ mPrefCategory.addPreference(mAllPref);
+ mPrefCategory.addPreference(mNonePref);
+
+ mAllController.displayPreference(mPreferenceScreen);
+ mPriorityController.displayPreference(mPreferenceScreen);
+ mNoneController.displayPreference(mPreferenceScreen);
+ }
+
+ private SelectorWithWidgetPreference makePreference(String key,
+ ZenModeAppsPreferenceController controller) {
+ final SelectorWithWidgetPreference pref = new SelectorWithWidgetPreference(mContext, false);
+ pref.setKey(key);
+ pref.setOnClickListener(controller.mSelectorClickListener);
+ return pref;
+ }
+
+ @Test
+ public void testUpdateState_All() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
+ .build())
+ .build(), true);
+ mAllController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testUpdateState_All_Unchecked() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build(), true);
+ mAllController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(false);
+ }
+
+ @Test
+ public void testUpdateState_None() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build(), true);
+ mNoneController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testUpdateState_None_Unchecked() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
+ .build())
+ .build(), true);
+ mNoneController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(false);
+ }
+
+ @Test
+ public void testUpdateState_Priority() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+ .build())
+ .build(), true);
+ mPriorityController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testUpdateState_Priority_Unchecked() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build(), true);
+ mPriorityController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(false);
+ }
+
+ @Test
+ public void testOnPreferenceChange_All() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
+ .build())
+ .build(), true);
+
+ mAllController.updateZenMode(preference, zenMode);
+ mAllController.onPreferenceChange(preference, true);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+
+ assertThat(captor.getValue().getPolicy().getAllowedChannels())
+ .isEqualTo(ZenMode.CHANNEL_POLICY_ALL);
+ }
+
+ @Test
+ public void testPreferenceClick_passesCorrectCheckedState_All() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build(), true);
+
+
+ mAllController.updateZenMode(mAllPref, zenMode);
+ mNoneController.updateZenMode(mNonePref, zenMode);
+ mPriorityController.updateZenMode(mPriorityPref, zenMode);
+
+ // MPME is checked; ALL and PRIORITY are unchecked.
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+
+ mPrefCategory.findPreference(KEY_ALL).performClick();
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ // Checks the policy value for ALL is set.
+ // The important part is that the interruption filter is propagated to the backend.
+ assertThat(captor.getValue().getRule().getInterruptionFilter())
+ .isEqualTo(INTERRUPTION_FILTER_ALL);
+ // ALL is now checked; others are unchecked.
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+ }
+
+ @Test
+ public void testPreferenceClick_passesCorrectCheckedState_None() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+ .build())
+ .build(), true);
+
+ mAllController.updateZenMode(mAllPref, zenMode);
+ mNoneController.updateZenMode(mNonePref, zenMode);
+ mPriorityController.updateZenMode(mPriorityPref, zenMode);
+
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+
+ // Click on NONE
+ mPrefCategory.findPreference(KEY_NONE).performClick();
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ // NONE is not actually propagated to the backend as an interruption filter;
+ // the filter is set to priority, and sounds and visual effects are disallowed.
+ // See AbstractZenModePreferenceController.
+ assertThat(captor.getValue().getRule().getInterruptionFilter())
+ .isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+ // NONE is now checked; others are unchecked.
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+ }
+
+ @Test
+ public void testPreferenceClick_passesCorrectCheckedState_Priority() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build(), true);
+
+ mAllController.updateZenMode(mAllPref, zenMode);
+ mNoneController.updateZenMode(mNonePref, zenMode);
+ mPriorityController.updateZenMode(mPriorityPref, zenMode);
+
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+
+ // Click on PRIORITY
+ mPrefCategory.findPreference(KEY_PRIORITY).performClick();
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ // Checks the policy value for PRIORITY is propagated to the backend.
+ assertThat(captor.getValue().getRule().getInterruptionFilter())
+ .isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+ // PRIORITY is now checked; others are unchecked.
+ assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
+ .isChecked());
+ assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+ .isChecked());
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java
new file mode 100644
index 00000000000..bda38437a37
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+import android.widget.Button;
+
+import com.android.settingslib.widget.LayoutPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@EnableFlags(Flags.FLAG_MODES_UI)
+@RunWith(RobolectricTestRunner.class)
+public final class ZenModeButtonPreferenceControllerTest {
+
+ private ZenModeButtonPreferenceController mController;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+
+ mController = new ZenModeButtonPreferenceController(
+ mContext, "something", mBackend);
+ }
+
+ @Test
+ public void isAvailable_notIfAppOptsOut() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(false)
+ .setEnabled(true)
+ .build(), false);
+ mController.setZenMode(zenMode);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_notIfModeDisabled() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(false)
+ .build(), false);
+ mController.setZenMode(zenMode);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_appOptedIn_modeEnabled() {
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(true)
+ .build(), false);
+ mController.setZenMode(zenMode);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void updateState_ruleActive() {
+ Button button = new Button(mContext);
+ LayoutPreference pref = mock(LayoutPreference.class);
+ when(pref.findViewById(anyInt())).thenReturn(button);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(true)
+ .build(), true);
+ mController.updateZenMode(pref, zenMode);
+ assertThat(button.getText().toString()).contains("off");
+ assertThat(button.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void updateState_ruleNotActive() {
+ Button button = new Button(mContext);
+ LayoutPreference pref = mock(LayoutPreference.class);
+ when(pref.findViewById(anyInt())).thenReturn(button);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(true)
+ .build(), false);
+ mController.updateZenMode(pref, zenMode);
+ assertThat(button.getText().toString()).contains("on");
+ assertThat(button.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void updateStateThenClick_ruleActive() {
+ Button button = new Button(mContext);
+ LayoutPreference pref = mock(LayoutPreference.class);
+ when(pref.findViewById(anyInt())).thenReturn(button);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(true)
+ .build(), true);
+ mController.updateZenMode(pref, zenMode);
+
+ button.callOnClick();
+ verify(mBackend).deactivateMode(zenMode);
+ }
+
+ @Test
+ public void updateStateThenClick_ruleNotActive() {
+ Button button = new Button(mContext);
+ LayoutPreference pref = mock(LayoutPreference.class);
+ when(pref.findViewById(anyInt())).thenReturn(button);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .setManualInvocationAllowed(true)
+ .setEnabled(true)
+ .build(), false);
+ mController.updateZenMode(pref, zenMode);
+
+ button.callOnClick();
+ verify(mBackend).activateMode(zenMode, null);
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
index 3e41778cdc0..d8c8bf0bfb5 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -22,6 +22,7 @@ import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.net.Uri;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -325,4 +327,46 @@ public class ZenModesSummaryHelperTest {
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications partially hidden, grayscale, and 2 more");
}
+
+ @Test
+ public void getAppsSummary_all() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("All");
+ }
+
+ @Test
+ public void getAppsSummary_none() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("None");
+ }
+
+ @Test
+ public void getAppsSummary_priorityApps() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("Selected apps");
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
index 09b1ea944a5..18dc00480b0 100644
--- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
@@ -166,6 +166,16 @@ public class BaseSearchIndexProviderTest {
return Collections.singletonList(sir);
}
+ @Override
+ public List getRawDataToIndex(Context context, boolean enabled) {
+ List rawData = super.getRawDataToIndex(context, enabled);
+ SearchIndexableRaw raw = new SearchIndexableRaw(context);
+ raw.key = TEST_PREF_KEY;
+ raw.title = "title";
+ rawData.add(raw);
+ return rawData;
+ }
+
@Override
protected boolean isPageSearchEnabled(Context context) {
return false;
@@ -176,6 +186,7 @@ public class BaseSearchIndexProviderTest {
provider.getNonIndexableKeys(RuntimeEnvironment.application);
assertThat(nonIndexableKeys).contains("status_header");
+ assertThat(nonIndexableKeys).contains(TEST_PREF_KEY);
}
@Test
diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateControllerTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateControllerTest.kt
deleted file mode 100644
index 5e486dd733e..00000000000
--- a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/ImsRegistrationStateControllerTest.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.deviceinfo.simstatus
-
-import android.content.Context
-import androidx.lifecycle.testing.TestLifecycleOwner
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.network.telephony.SimSlotRepository
-import com.android.settings.network.telephony.ims.ImsMmTelRepository
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-class ImsRegistrationStateControllerTest {
-
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- private val mockSimSlotRepository = mock {
- on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
- }
-
- private val mockImsMmTelRepository = mock {
- on { imsRegisteredFlow() } doReturn flowOf(true)
- }
-
- private val controller = ImsRegistrationStateController(
- context = context,
- simSlotRepository = mockSimSlotRepository,
- imsMmTelRepositoryFactory = { subId ->
- assertThat(subId).isEqualTo(SUB_ID)
- mockImsMmTelRepository
- },
- )
-
- @Test
- fun collectImsRegistered() = runBlocking {
- var imsRegistered = false
-
- controller.collectImsRegistered(TestLifecycleOwner(), SIM_SLOT_INDEX) {
- imsRegistered = it
- }
- delay(100)
-
- assertThat(imsRegistered).isTrue()
- }
-
- private companion object {
- const val SIM_SLOT_INDEX = 0
- const val SUB_ID = 1
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepositoryTest.kt
new file mode 100644
index 00000000000..d7486604f10
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SignalStrengthRepositoryTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.deviceinfo.simstatus
+
+import android.content.Context
+import android.telephony.CellSignalStrengthCdma
+import android.telephony.CellSignalStrengthGsm
+import android.telephony.CellSignalStrengthLte
+import android.telephony.CellSignalStrengthNr
+import android.telephony.CellSignalStrengthTdscdma
+import android.telephony.CellSignalStrengthWcdma
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class SignalStrengthRepositoryTest {
+
+ private var signalStrength = SignalStrength()
+
+ private val mockTelephonyManager = mock {
+ on { createForSubscriptionId(SUB_ID) } doReturn mock
+ on { registerTelephonyCallback(any(), any()) } doAnswer {
+ val listener = it.getArgument(1)
+ listener.onSignalStrengthsChanged(signalStrength)
+ }
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(TelephonyManager::class.java) } doAnswer { mockTelephonyManager }
+ }
+
+ private val serviceState = ServiceState()
+
+ private val repository = SignalStrengthRepository(context) { flowOf(serviceState) }
+
+ @Test
+ fun signalStrengthDisplayFlow_serviceStatePowerOff() = runBlocking {
+ serviceState.state = ServiceState.STATE_POWER_OFF
+
+ val signalStrength = repository.signalStrengthDisplayFlow(SUB_ID).firstWithTimeoutOrNull()
+
+ assertThat(signalStrength).isEqualTo("0")
+ }
+
+ @Test
+ fun signalStrengthDisplayFlow_lteWcdma() = runBlocking {
+ serviceState.state = ServiceState.STATE_IN_SERVICE
+ signalStrength = SignalStrength(
+ CellSignalStrengthCdma(),
+ CellSignalStrengthGsm(),
+ mock {
+ on { isValid } doReturn true
+ on { dbm } doReturn 40
+ on { asuLevel } doReturn 41
+ },
+ CellSignalStrengthTdscdma(),
+ mock {
+ on { isValid } doReturn true
+ on { dbm } doReturn 50
+ on { asuLevel } doReturn 51
+ },
+ CellSignalStrengthNr(),
+ )
+
+ val signalStrength = repository.signalStrengthDisplayFlow(SUB_ID).firstWithTimeoutOrNull()
+
+ assertThat(signalStrength).isEqualTo("50 dBm 51 asu")
+ }
+
+ @Test
+ fun signalStrengthDisplayFlow_lteCdma() = runBlocking {
+ serviceState.state = ServiceState.STATE_IN_SERVICE
+ signalStrength = SignalStrength(
+ mock {
+ on { isValid } doReturn true
+ on { dbm } doReturn 30
+ on { asuLevel } doReturn 31
+ },
+ CellSignalStrengthGsm(),
+ CellSignalStrengthWcdma(),
+ CellSignalStrengthTdscdma(),
+ mock {
+ on { isValid } doReturn true
+ on { dbm } doReturn 50
+ on { asuLevel } doReturn 51
+ },
+ CellSignalStrengthNr(),
+ )
+
+ val signalStrength = repository.signalStrengthDisplayFlow(SUB_ID).firstWithTimeoutOrNull()
+
+ assertThat(signalStrength).isEqualTo("50 dBm 51 asu")
+ }
+
+ @Test
+ fun signalStrengthDisplayFlow_lteOnly() = runBlocking {
+ serviceState.state = ServiceState.STATE_IN_SERVICE
+ signalStrength = SignalStrength(
+ CellSignalStrengthCdma(),
+ CellSignalStrengthGsm(),
+ CellSignalStrengthWcdma(),
+ CellSignalStrengthTdscdma(),
+ mock {
+ on { isValid } doReturn true
+ on { dbm } doReturn 50
+ on { asuLevel } doReturn 51
+ },
+ CellSignalStrengthNr(),
+ )
+
+ val signalStrength = repository.signalStrengthDisplayFlow(SUB_ID).firstWithTimeoutOrNull()
+
+ assertThat(signalStrength).isEqualTo("50 dBm 51 asu")
+ }
+
+ private companion object {
+ const val SUB_ID = 1
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt
new file mode 100644
index 00000000000..01f32bfccf0
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.deviceinfo.simstatus
+
+import android.content.Context
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.deviceinfo.simstatus.SimStatusDialogRepository.SimStatusDialogInfo
+import com.android.settings.network.telephony.SimSlotRepository
+import com.android.settings.network.telephony.ims.ImsMmTelRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.anyVararg
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class SimStatusDialogRepositoryTest {
+
+ private val carrierConfig = PersistableBundle().apply {
+ putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true)
+ }
+
+ private val mockCarrierConfigManager = mock {
+ on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
+ }
+
+ private val mockSimSlotRepository = mock {
+ on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
+ }
+
+ private val mockSignalStrengthRepository = mock {
+ on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH)
+ }
+
+ private val mockImsMmTelRepository = mock {
+ on { imsRegisteredFlow() } doReturn flowOf(true)
+ }
+
+ private val controller = SimStatusDialogRepository(
+ context = context,
+ simSlotRepository = mockSimSlotRepository,
+ signalStrengthRepository = mockSignalStrengthRepository,
+ imsMmTelRepositoryFactory = { subId ->
+ assertThat(subId).isEqualTo(SUB_ID)
+ mockImsMmTelRepository
+ },
+ )
+
+ @Test
+ fun collectSimStatusDialogInfo() = runBlocking {
+ var simStatusDialogInfo = SimStatusDialogInfo()
+
+ controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
+ simStatusDialogInfo = it
+ }
+ delay(100)
+
+ assertThat(simStatusDialogInfo).isEqualTo(
+ SimStatusDialogInfo(
+ signalStrength = SIGNAL_STRENGTH,
+ imsRegistered = true,
+ )
+ )
+ }
+
+ @Test
+ fun collectSimStatusDialogInfo_doNotShowSignalStrength() = runBlocking {
+ carrierConfig.putBoolean(
+ CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
+ false
+ )
+ var simStatusDialogInfo = SimStatusDialogInfo()
+
+ controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
+ simStatusDialogInfo = it
+ }
+ delay(100)
+
+ assertThat(simStatusDialogInfo.signalStrength).isNull()
+ }
+
+ @Test
+ fun collectSimStatusDialogInfo_doNotShowImsRegistration() = runBlocking {
+ carrierConfig.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false)
+ var simStatusDialogInfo = SimStatusDialogInfo()
+
+ controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
+ simStatusDialogInfo = it
+ }
+ delay(100)
+
+ assertThat(simStatusDialogInfo.imsRegistered).isNull()
+ }
+
+ private companion object {
+ const val SIM_SLOT_INDEX = 0
+ const val SUB_ID = 1
+
+ const val SIGNAL_STRENGTH = "-82 dBm 58 asu"
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index 43270c2433d..79f53cab4ad 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -19,20 +19,30 @@ package com.android.settings.spa.network
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
+import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.KEYCODE_FORWARD_DEL
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.NativeKeyEvent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performKeyPress
+import androidx.compose.ui.test.performTextClearance
+import androidx.compose.ui.test.performTextInput
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import com.android.settingslib.spa.testutils.waitUntilExists
+import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -125,22 +135,7 @@ class SimOnboardingLabelSimTest {
@Test
fun simOnboardingLabelSimImpl_showItem_show3Items() {
- mockSimOnboardingService.stub {
- on { targetSubId }.doReturn(SUB_ID_1)
- on { targetSubInfo }.doReturn(SUB_INFO_1)
- on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
- on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfoList() }.doReturn(
- listOf(
- SUB_INFO_1,
- SUB_INFO_2,
- SUB_INFO_3
- )
- )
- on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
- on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
- on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
- }
+ preSetupContent()
composeTestRule.setContent {
CompositionLocalProvider(
@@ -161,6 +156,118 @@ class SimOnboardingLabelSimTest {
@Test
fun simOnboardingLabelSimImpl_showDialog_checkTitle() {
+ preSetupContent()
+
+ composeTestRule.setContent {
+ SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
+ }
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
+
+ composeTestRule.onNodeWithText(
+ context.getString(R.string.sim_onboarding_label_sim_dialog_title)
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun showDialog_noContentInput_showOriginalDisplayName() {
+ preSetupContent()
+
+ composeTestRule.setContent {
+ SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
+ }
+
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
+
+ assertEquals(
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
+ .fetchSemanticsNode()
+ .config[SemanticsProperties.EditableText].text, DISPLAY_NAME_1
+ )
+ }
+
+ @Test
+ fun showDialog_clearContent_showOriginalDisplayName() {
+ preSetupContent()
+
+ composeTestRule.setContent {
+ SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
+ }
+
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextClearance()
+
+ assertEquals(
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
+ .fetchSemanticsNode()
+ .config[SemanticsProperties.EditableText].text, DISPLAY_NAME_1
+ )
+ }
+
+ @Test
+ fun showDialog_modifyContent_showModifiedDisplayName() {
+ val inputData = "input_data"
+ preSetupContent()
+
+ composeTestRule.setContent {
+ SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
+ }
+
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextInput(inputData)
+
+ // Due to this TextField with Text and EditText, it need fetch correct node to get correct
+ // content.
+ assertEquals(
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
+ .fetchSemanticsNode()
+ .config[SemanticsProperties.EditableText].text, inputData + DISPLAY_NAME_1
+ )
+
+ // Click save button
+ composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
+ .performClick()
+
+ // Check preference's name is still DISPLAY_NAME_1
+ composeTestRule.onNodeWithText(inputData + DISPLAY_NAME_1).assertExists()
+ }
+
+ @Test
+ fun showDialog_onlySpaceCharContent_showAndSaveOriginalDisplayName() {
+ val spaceChars = " ";
+ preSetupContent()
+
+ composeTestRule.setContent {
+ SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
+ }
+
+ // Simulate real operation,
+ // 1. Click preference of DISPLAY_NAME_1
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
+ // 2. Input space chars to EditText view
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextInput(spaceChars)
+ // 3. Remove the string of DISPLAY_NAME_1 from EditText view
+ repeat(DISPLAY_NAME_1.length) {
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
+ .performKeyPress(KeyEvent(NativeKeyEvent(ACTION_DOWN, KEYCODE_FORWARD_DEL)))
+ }
+
+ // Due to this TextField with Text and EditText, it need fetch correct node to get correct
+ // content.
+ assertEquals(
+ DISPLAY_NAME_1, composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
+ .fetchSemanticsNode()
+ .config[SemanticsProperties.EditableText].text
+ )
+
+ // Click save button
+ composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
+ .performClick()
+
+ // Check preference's name is still DISPLAY_NAME_1
+ composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertExists()
+ }
+
+ fun preSetupContent() {
mockSimOnboardingService.stub {
on { targetSubId }.doReturn(SUB_ID_1)
on { targetSubInfo }.doReturn(SUB_INFO_1)
@@ -177,21 +284,10 @@ class SimOnboardingLabelSimTest {
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
}
-
- composeTestRule.setContent {
- SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
- }
-
-
- composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
-
- composeTestRule.onNodeWithText(
- context.getString(R.string.sim_onboarding_label_sim_dialog_title)
- )
- .assertIsDisplayed()
}
private companion object {
+ const val TEXT_FIELD_INPUT = "contentInput"
const val SUB_ID_1 = 1
const val SUB_ID_2 = 2
const val SUB_ID_3 = 3
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
index d1847c8d48f..1395ed4b373 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
@@ -91,6 +91,7 @@ class SimOnboardingPageProviderTest {
mockSimOnboardingService.stub {
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
on { isAllOfSlotAssigned }.thenReturn(true)
+ on { doesTargetSimActive }.thenReturn(false)
}
composeTestRule.setContent {
diff --git a/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
index 2f52031e7aa..a962d4986e7 100644
--- a/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
@@ -27,6 +27,7 @@ import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
@@ -43,6 +44,28 @@ class ClientInitiatedActionRepositoryTest {
private val repository = ClientInitiatedActionRepository(context)
+ @Test
+ fun onSystemUpdate_runtimeException_doNothing() {
+ mockCarrierConfigManager.stub {
+ on { getConfigForSubId(any(), any()) } doThrow(RuntimeException())
+ }
+
+ repository.onSystemUpdate()
+
+ verify(context, never()).sendBroadcast(any())
+ }
+
+ @Test
+ fun onSystemUpdate_illegalStateException_doNothing() {
+ mockCarrierConfigManager.stub {
+ on { getConfigForSubId(any(), any()) } doThrow(IllegalStateException())
+ }
+
+ repository.onSystemUpdate()
+
+ verify(context, never()).sendBroadcast(any())
+ }
+
@Test
fun onSystemUpdate_notEnabled() {
mockCarrierConfigManager.stub {
diff --git a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
index 364b17cc646..d068194cd9c 100644
--- a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
@@ -25,9 +25,10 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.view.accessibility.Flags;
@@ -61,7 +62,7 @@ public class PreferredShortcutsTest {
private static final ContentResolver sContentResolver =
ApplicationProvider.getApplicationContext().getContentResolver();
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Before
@@ -174,7 +175,7 @@ public class PreferredShortcutsTest {
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @RequiresFlagsEnabled(Flags.FLAG_A11Y_QS_SHORTCUT)
public void updatePreferredShortcutFromSettings_colorInversionWithQsAndSoftwareShortcut_preferredShortcutsMatches() {
String target = COLOR_INVERSION_COMPONENT_NAME.flattenToString();
Settings.Secure.putString(sContentResolver,
@@ -192,7 +193,7 @@ public class PreferredShortcutsTest {
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @RequiresFlagsDisabled(Flags.FLAG_A11Y_QS_SHORTCUT)
public void updatePreferredShortcutFromSettings_colorInversionWithQsAndHardwareShortcut_qsShortcutNotSaved() {
String target = COLOR_INVERSION_COMPONENT_NAME.flattenToString();
Settings.Secure.putString(sContentResolver,
diff --git a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
index f65480d3911..3fa380828b9 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
@@ -28,26 +28,18 @@ import static com.android.settings.deviceinfo.simstatus.SimStatusDialogControlle
import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.OPERATOR_INFO_VALUE_ID;
import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.ROAMING_INFO_VALUE_ID;
import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.SERVICE_STATE_VALUE_ID;
-import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.SIGNAL_STRENGTH_LABEL_ID;
-import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.SIGNAL_STRENGTH_VALUE_ID;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.CellSignalStrength;
import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -71,7 +63,6 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -87,14 +78,6 @@ public class SimStatusDialogControllerTest {
@Mock
private ServiceState mServiceState;
@Mock
- private SignalStrength mSignalStrength;
- @Mock
- private CellSignalStrength mCellSignalStrengthCdma;
- @Mock
- private CellSignalStrength mCellSignalStrengthLte;
- @Mock
- private CellSignalStrength mCellSignalStrengthWcdma;
- @Mock
private CarrierConfigManager mCarrierConfigManager;
private PersistableBundle mPersistableBundle;
@Mock
@@ -149,15 +132,6 @@ public class SimStatusDialogControllerTest {
mUpdatePhoneNumberCount.incrementAndGet();
}
};
- // CellSignalStrength setup
- doReturn(0).when(mCellSignalStrengthCdma).getDbm();
- doReturn(0).when(mCellSignalStrengthCdma).getAsuLevel();
- doReturn(0).when(mCellSignalStrengthLte).getDbm();
- doReturn(0).when(mCellSignalStrengthLte).getAsuLevel();
- doReturn(0).when(mCellSignalStrengthWcdma).getDbm();
- doReturn(0).when(mCellSignalStrengthWcdma).getAsuLevel();
-
- doReturn(null).when(mSignalStrength).getCellSignalStrengths();
doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
when(mTelephonyManager.getActiveModemCount()).thenReturn(MAX_PHONE_COUNT_SINGLE_SIM);
@@ -172,10 +146,7 @@ public class SimStatusDialogControllerTest {
mPersistableBundle = new PersistableBundle();
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mPersistableBundle);
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true);
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
- doReturn(mSignalStrength).when(mTelephonyManager).getSignalStrength();
}
@Test
@@ -218,7 +189,7 @@ public class SimStatusDialogControllerTest {
@Test
@Ignore("b/337417520")
- public void initialize_updateServiceStateWithPowerOff_shouldUpdateTextAndResetSignalStrength() {
+ public void initialize_updateServiceStateWithPowerOff_shouldUpdateText() {
when(mServiceState.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
mController.initialize();
@@ -226,12 +197,11 @@ public class SimStatusDialogControllerTest {
final String offServiceText = ResourcesUtils.getResourcesString(
mContext, "radioInfo_service_off");
verify(mDialog).setText(SERVICE_STATE_VALUE_ID, offServiceText);
- verify(mDialog).setText(SIGNAL_STRENGTH_VALUE_ID, "0");
}
@Test
@Ignore("b/337417520")
- public void initialize_updateVoiceDataOutOfService_shouldUpdateSettingAndResetSignalStrength() {
+ public void initialize_updateVoiceDataOutOfService_shouldUpdateSetting() {
when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getDataRegistrationState()).thenReturn(
ServiceState.STATE_OUT_OF_SERVICE);
@@ -241,7 +211,6 @@ public class SimStatusDialogControllerTest {
final String offServiceText = ResourcesUtils.getResourcesString(
mContext, "radioInfo_service_out");
verify(mDialog).setText(SERVICE_STATE_VALUE_ID, offServiceText);
- verify(mDialog).setText(SIGNAL_STRENGTH_VALUE_ID, "0");
}
@Test
@@ -256,52 +225,6 @@ public class SimStatusDialogControllerTest {
verify(mDialog).setText(SERVICE_STATE_VALUE_ID, inServiceText);
}
- @Test
- public void initialize_updateSignalStrengthWithLte50Wcdma40_shouldUpdateSignalStrengthTo50() {
- final int lteDbm = 50;
- final int lteAsu = 50;
- final int wcdmaDbm = 40;
- final int wcdmaAsu = 40;
- setupCellSignalStrength_lteWcdma(lteDbm, lteAsu, wcdmaDbm, wcdmaAsu);
-
- mController.initialize();
-
- final String signalStrengthString = ResourcesUtils.getResourcesString(
- mContext, "sim_signal_strength", lteDbm, lteAsu);
- verify(mDialog, times(2)).setText(SIGNAL_STRENGTH_VALUE_ID, signalStrengthString);
- }
-
- @Test
- public void initialize_updateSignalStrengthWithLte50Cdma30_shouldUpdateSignalStrengthTo50() {
- final int lteDbm = 50;
- final int lteAsu = 50;
- final int cdmaDbm = 30;
- final int cdmaAsu = 30;
- setupCellSignalStrength_lteCdma(lteDbm, lteAsu, cdmaDbm, cdmaAsu);
-
- mController.initialize();
-
- final String signalStrengthString = ResourcesUtils.getResourcesString(
- mContext, "sim_signal_strength", lteDbm, lteAsu);
- verify(mDialog, times(2)).setText(SIGNAL_STRENGTH_VALUE_ID, signalStrengthString);
- }
-
- @Test
- public void initialize_updateVoiceOutOfServiceDataInService_shouldUpdateSignalStrengthTo50() {
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
- when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-
- final int lteDbm = 50;
- final int lteAsu = 50;
- setupCellSignalStrength_lteOnly(lteDbm, lteAsu);
-
- mController.initialize();
-
- final String signalStrengthString = ResourcesUtils.getResourcesString(
- mContext, "sim_signal_strength", lteDbm, lteAsu);
- verify(mDialog, times(2)).setText(SIGNAL_STRENGTH_VALUE_ID, signalStrengthString);
- }
-
@Test
public void initialize_updateVoiceNetworkTypeWithEdge_shouldUpdateSettingToEdge() {
when(mTelephonyManager.getVoiceNetworkType()).thenReturn(
@@ -357,17 +280,6 @@ public class SimStatusDialogControllerTest {
verify(mDialog).removeSettingFromScreen(ICCID_INFO_VALUE_ID);
}
- @Test
- public void initialize_doNotShowSignalStrength_shouldRemoveSignalStrengthSetting() {
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, false);
-
- mController.initialize();
-
- verify(mDialog, times(2)).removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID);
- verify(mDialog, times(2)).removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID);
- }
-
@Test
public void initialize_showSignalStrengthAndIccId_shouldShowSignalStrengthAndIccIdSetting() {
// getConfigForSubId is nullable, so make sure the default behavior is correct
@@ -375,7 +287,6 @@ public class SimStatusDialogControllerTest {
mController.initialize();
- verify(mDialog, times(2)).setText(eq(SIGNAL_STRENGTH_VALUE_ID), any());
verify(mDialog).removeSettingFromScreen(ICCID_INFO_LABEL_ID);
verify(mDialog).removeSettingFromScreen(ICCID_INFO_VALUE_ID);
}
@@ -394,8 +305,6 @@ public class SimStatusDialogControllerTest {
@Test
@Ignore
public void initialize_imsRegistered_shouldSetImsRegistrationStateSummaryToRegisterd() {
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
when(mTelephonyManager.isImsRegistered(anyInt())).thenReturn(true);
mController.initialize();
@@ -407,8 +316,6 @@ public class SimStatusDialogControllerTest {
@Test
@Ignore
public void initialize_imsNotRegistered_shouldSetImsRegistrationStateSummaryToNotRegisterd() {
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
when(mTelephonyManager.isImsRegistered(anyInt())).thenReturn(false);
mController.initialize();
@@ -418,67 +325,19 @@ public class SimStatusDialogControllerTest {
}
@Test
- public void initialize_showImsRegistration_shouldNotRemoveImsRegistrationStateSetting() {
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
-
+ @Ignore("b/337417520")
+ public void initialize_showImsRegistration_shouldShowImsRegistrationStateSetting() {
mController.initialize();
- verify(mDialog, never()).removeSettingFromScreen(IMS_REGISTRATION_STATE_VALUE_ID);
+ verify(mDialog).setSettingVisibility(IMS_REGISTRATION_STATE_VALUE_ID, true);
}
@Test
- public void initialize_doNotShowImsRegistration_shouldRemoveImsRegistrationStateSetting() {
- mPersistableBundle.putBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
-
+ @Ignore("b/337417520")
+ public void initialize_doNotShowImsRegistration_shouldHideImsRegistrationStateSetting() {
mController.initialize();
- verify(mDialog).removeSettingFromScreen(IMS_REGISTRATION_STATE_LABEL_ID);
- verify(mDialog).removeSettingFromScreen(IMS_REGISTRATION_STATE_VALUE_ID);
- }
-
- @Test
- public void initialize_nullSignalStrength_noCrash() {
- doReturn(null).when(mTelephonyManager).getSignalStrength();
- // we should not crash when running the following line
- mController.initialize();
- }
-
- private void setupCellSignalStrength_lteWcdma(int lteDbm, int lteAsu, int wcdmaDbm,
- int wcdmaAsu) {
- doReturn(lteDbm).when(mCellSignalStrengthLte).getDbm();
- doReturn(lteAsu).when(mCellSignalStrengthLte).getAsuLevel();
- doReturn(wcdmaDbm).when(mCellSignalStrengthWcdma).getDbm();
- doReturn(wcdmaAsu).when(mCellSignalStrengthWcdma).getAsuLevel();
-
- List cellSignalStrengthList = new ArrayList<>(2);
- cellSignalStrengthList.add(mCellSignalStrengthLte);
- cellSignalStrengthList.add(mCellSignalStrengthWcdma);
-
- doReturn(cellSignalStrengthList).when(mSignalStrength).getCellSignalStrengths();
- }
-
- private void setupCellSignalStrength_lteCdma(int lteDbm, int lteAsu, int cdmaDbm, int cdmaAsu) {
- doReturn(lteDbm).when(mCellSignalStrengthLte).getDbm();
- doReturn(lteAsu).when(mCellSignalStrengthLte).getAsuLevel();
- doReturn(cdmaDbm).when(mCellSignalStrengthCdma).getDbm();
- doReturn(cdmaAsu).when(mCellSignalStrengthCdma).getAsuLevel();
-
- List cellSignalStrengthList = new ArrayList<>(2);
- cellSignalStrengthList.add(mCellSignalStrengthLte);
- cellSignalStrengthList.add(mCellSignalStrengthCdma);
-
- doReturn(cellSignalStrengthList).when(mSignalStrength).getCellSignalStrengths();
- }
-
- private void setupCellSignalStrength_lteOnly(int lteDbm, int lteAsu) {
- doReturn(lteDbm).when(mCellSignalStrengthLte).getDbm();
- doReturn(lteAsu).when(mCellSignalStrengthLte).getAsuLevel();
-
- List cellSignalStrengthList = new ArrayList<>(2);
- cellSignalStrengthList.add(mCellSignalStrengthLte);
-
- doReturn(cellSignalStrengthList).when(mSignalStrength).getCellSignalStrengths();
+ verify(mDialog).setSettingVisibility(IMS_REGISTRATION_STATE_LABEL_ID, false);
+ verify(mDialog).setSettingVisibility(IMS_REGISTRATION_STATE_VALUE_ID, false);
}
}
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index 522dcd5e242..36edfa0e9e1 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -22,7 +22,7 @@ import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DISABLED_VAL;
-import static com.android.settings.privatespace.PrivateSpaceMaintainer.PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.PRIVATE_SPACE_CREATE_AUTO_LOCK_VAL;
import static com.google.common.truth.Truth.assertThat;
@@ -336,9 +336,9 @@ public class PrivateSpaceMaintainerTest {
privateSpaceMaintainer.deletePrivateSpace();
privateSpaceMaintainer.createPrivateSpace();
assertThat(privateSpaceMaintainer.getPrivateSpaceAutoLockSetting())
- .isEqualTo(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+ .isEqualTo(PRIVATE_SPACE_CREATE_AUTO_LOCK_VAL);
assertThat(Settings.Secure.getInt(mContentResolver, PRIVATE_SPACE_AUTO_LOCK, -1))
- .isEqualTo(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+ .isEqualTo(PRIVATE_SPACE_CREATE_AUTO_LOCK_VAL);
}
/**