diff options
108 files changed, 1127 insertions, 974 deletions
diff --git a/java/res/layout/chooser_grid_preview_files_text.xml b/java/res/layout/chooser_grid_preview_files_text.xml index 2756e800..65c62f82 100644 --- a/java/res/layout/chooser_grid_preview_files_text.xml +++ b/java/res/layout/chooser_grid_preview_files_text.xml @@ -53,6 +53,7 @@ android:maxLines="@integer/text_preview_lines" android:ellipsize="end" android:linksClickable="false" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" android:textAppearance="@style/TextAppearance.ChooserDefault"/> </LinearLayout> diff --git a/java/res/layout/resolver_empty_states.xml b/java/res/layout/resolver_empty_states.xml index d77630ee..0cf6e955 100644 --- a/java/res/layout/resolver_empty_states.xml +++ b/java/res/layout/resolver_empty_states.xml @@ -79,13 +79,13 @@ android:layout_centerHorizontal="true" android:layout_below="@androidprv:id/resolver_empty_state_subtitle" android:indeterminateTint="?android:attr/colorAccent"/> + <TextView + android:id="@android:id/empty" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/noApplications" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" + android:padding="@dimen/chooser_edge_margin_normal" + android:gravity="center"/> </RelativeLayout> - <TextView android:id="@android:id/empty" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:attr/colorBackground" - android:text="@string/noApplications" - android:padding="@dimen/chooser_edge_margin_normal" - android:layout_marginBottom="56dp" - android:gravity="center"/> </RelativeLayout> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 7482d692..5a1a8938 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ከቆመበት ቀጥል"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"ምንም የሥራ መተግበሪያዎች የሉም"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"ምንም የግል መተግበሪያዎች የሉም"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"ምንም የግል መተግበሪያዎች የሉም"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> በግል መገለጫዎ ውስጥ ይከፈት?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> በስራ መገለጫዎ ውስጥ ይከፈት?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"የግል አሳሽ ተጠቀም"</string> diff --git a/java/res/values-as/strings.xml b/java/res/values-as/strings.xml index 67bbf6c8..10a68eb7 100644 --- a/java/res/values-as/strings.xml +++ b/java/res/values-as/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"আনপজ কৰক"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"কোনো কৰ্মস্থানৰ এপ্ নাই"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"কোনো ব্যক্তিগত এপ্ নাই"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"কোনো ব্যক্তিগত এপ্নাই"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"আপোনাৰ ব্যক্তিগত প্ৰ’ফাইলত <xliff:g id="APP">%s</xliff:g> খুলিবনে?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"আপোনাৰ কর্মস্থানৰ প্ৰ\'ফাইলত <xliff:g id="APP">%s</xliff:g> খুলিবনে?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ব্যক্তিগত ব্ৰাউজাৰ ব্যৱহাৰ কৰক"</string> diff --git a/java/res/values-az/strings.xml b/java/res/values-az/strings.xml index a073ea20..4f42c0e3 100644 --- a/java/res/values-az/strings.xml +++ b/java/res/values-az/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Tətbiqə qeydə almaq icazəsi verilməsə də, bu USB vasitəsilə səsi qeydə ala bilər."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Şəxsi"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"İş"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Şəxsi"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Məxfi"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Şəxsi məzmuna baxış"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"İş məzmununa baxış"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Şəxsi baxış"</string> diff --git a/java/res/values-b+sr+Latn/strings.xml b/java/res/values-b+sr+Latn/strings.xml index 4f016f19..a2e81609 100644 --- a/java/res/values-b+sr+Latn/strings.xml +++ b/java/res/values-b+sr+Latn/strings.xml @@ -68,7 +68,7 @@ <string name="sharing_files_with_link" msgid="6052797122358827239">"{count,plural, =1{Deli se fajl sa linkom}one{Deli se # fajl sa linkom}few{Dele se # fajla sa linkom}other{Deli se # fajlova sa linkom}}"</string> <string name="sharing_album" msgid="191743129899503345">"Deljeni album"</string> <string name="sharing_images_only" msgid="7762589767189955438">"{count,plural, =1{Samo slika}one{Samo slike}few{Samo slike}other{Samo slike}}"</string> - <string name="sharing_videos_only" msgid="5549729252364968606">"{count,plural, =1{Samo video}one{Samo video snimci}few{Samo video snimci}other{Samo video snimci}}"</string> + <string name="sharing_videos_only" msgid="5549729252364968606">"{count,plural, =1{Samo video}one{Samo videi}few{Samo videi}other{Samo videi}}"</string> <string name="sharing_files_only" msgid="6603666533766964768">"{count,plural, =1{Samo fajl}one{Samo fajlovi}few{Samo fajlovi}other{Samo fajlovi}}"</string> <string name="image_preview_a11y_description" msgid="297102643932491797">"Sličica za pregled slike"</string> <string name="video_preview_a11y_description" msgid="683440858811095990">"Sličica za pregled videa"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Ponovo aktiviraj"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nema poslovnih aplikacija"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nema ličnih aplikacija"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Bez privatnih aplikacija"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Želite da na ličnom profilu otvorite: <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Želite da na poslovnom profilu otvorite: <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Koristi lični pregledač"</string> diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml index 9ab80a2f..f5f38d04 100644 --- a/java/res/values-be/strings.xml +++ b/java/res/values-be/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Уключыць"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Няма працоўных праграм"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Няма асабістых праграм"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Даступных прыватных праграм няма"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Адкрыць праграму \"<xliff:g id="APP">%s</xliff:g>\" з выкарыстаннем асабістага профілю?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Адкрыць праграму \"<xliff:g id="APP">%s</xliff:g>\" з выкарыстаннем працоўнага профілю?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Скарыстаць асабісты браўзер"</string> diff --git a/java/res/values-bn/strings.xml b/java/res/values-bn/strings.xml index 83c76752..fc3f1f08 100644 --- a/java/res/values-bn/strings.xml +++ b/java/res/values-bn/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"এই দিয়ে ছবি তুলুন"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> দিয়ে ছবি তুলুন"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"ছবি তুলুন"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"আলাদা কোনো অ্যাপ্লিকেশান ব্যবহার করুন"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"আলাদা কোনও অ্যাপ ব্যবহার করুন"</string> <string name="chooseActivity" msgid="6659724877523973446">"একটি অ্যাকশন বেছে নিন"</string> <string name="noApplications" msgid="1139487441772284671">"কোনো অ্যাপ্লিকেশানই এই ক্রিয়া সঞ্চালন করতে পারবে না৷"</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"আপনি এই অ্যাপ্লিকেশানটি আপনার কর্মস্থলের প্রোফাইলের বাইরে ব্যবহার করছেন"</string> @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"এই অ্যাপকে রেকর্ড করার অনুমতি দেওয়া হয়নি কিন্তু USB ডিভাইসের মাধ্যমে সেটি অডিও রেকর্ড করতে পারে।"</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"ব্যক্তিগত"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"অফিস"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"ব্যক্তিগত"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"প্রাইভেট"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"ব্যক্তিগত ভিউ"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"অফিসের ভিউ"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"ব্যক্তিগত ভিউ"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"আনপজ করুন"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"এর জন্য কোনও অফিস অ্যাপ নেই"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"ব্যক্তিগত অ্যাপে দেখা যাবে না"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"কোনও ব্যক্তিগত অ্যাপ নেই"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"আপনার ব্যক্তিগত প্রোফাইল থেকে <xliff:g id="APP">%s</xliff:g> খুলবেন?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"আপনার অফিস প্রোফাইল থেকে <xliff:g id="APP">%s</xliff:g> খুলবেন?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ব্যক্তিগত ব্রাউজার ব্যবহার করুন"</string> diff --git a/java/res/values-bs/strings.xml b/java/res/values-bs/strings.xml index a29f3638..30526e41 100644 --- a/java/res/values-bs/strings.xml +++ b/java/res/values-bs/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Ponovo pokreni"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nema poslovnih aplikacija"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nema ličnih aplikacija"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nema privatnih aplikacija"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Otvoriti aplikaciju <xliff:g id="APP">%s</xliff:g> na ličnom profilu?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Otvoriti aplikaciju <xliff:g id="APP">%s</xliff:g> na radnom profilu?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Koristi lični preglednik"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index daac39ef..d499765a 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reactiva"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Cap aplicació de treball"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Cap aplicació personal"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Cap aplicació privada"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Vols obrir <xliff:g id="APP">%s</xliff:g> al teu perfil personal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Vols obrir <xliff:g id="APP">%s</xliff:g> al teu perfil de treball?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Utilitza el navegador personal"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 4f0eca35..58b788b2 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Zrušit pozastavení"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Žádné pracovní aplikace"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Žádné osobní aplikace"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Žádné soukromé aplikace"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Otevřít aplikaci <xliff:g id="APP">%s</xliff:g> v osobním profilu?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Otevřít aplikaci <xliff:g id="APP">%s</xliff:g> v pracovním profilu?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Použít osobní prohlížeč"</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 784a2efd..ba820621 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Genoptag"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Der er ingen arbejdsapps"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Der er ingen personlige apps"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Ingen private apps"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Vil du åbne <xliff:g id="APP">%s</xliff:g> på din personlige profil?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Vil du åbne <xliff:g id="APP">%s</xliff:g> på din arbejdsprofil?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Brug personlig browser"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 07be8072..54c3cb4d 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Diese App hat noch keine Berechtigung zum Aufnehmen erhalten, könnte aber Audioaufnahmen über dieses USB-Gerät machen."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Privat"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Geschäftlich"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Privat"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Vertraulich"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Private Ansicht"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Geschäftliche Ansicht"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Private Ansicht"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Nicht mehr pausieren"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Keine geschäftlichen Apps"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Keine privaten Apps"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Keine internen Apps"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> in deinem privaten Profil öffnen?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> in deinem Arbeitsprofil öffnen?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Privaten Browser verwenden"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index b62d6687..c8c6ebf0 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Αναίρεση παύσης"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Δεν υπάρχουν εφαρμογές εργασιών"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Δεν υπάρχουν προσωπικές εφαρμογές"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Καμία ιδιωτική εφαρμογή"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Θέλετε να ανοίξετε την εφαρμογή <xliff:g id="APP">%s</xliff:g> στο προσωπικό σας προφίλ;"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Θέλετε να ανοίξετε την εφαρμογή <xliff:g id="APP">%s</xliff:g> στο προφίλ σας εργασίας;"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Χρήση προσωπικού προγράμματος περιήγησης"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 2f33f965..68b36f05 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reanudar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"El contenido no es compatible con apps de trabajo"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"El contenido no es compatible con apps personales"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"No hay apps privadas"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"¿Quieres abrir <xliff:g id="APP">%s</xliff:g> en tu perfil personal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"¿Quieres abrir <xliff:g id="APP">%s</xliff:g> en tu perfil de trabajo?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usar un navegador personal"</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 92aed933..db44e905 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reactivar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ninguna aplicación de trabajo"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Ninguna aplicación personal"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"No hay aplicaciones privadas"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"¿Abrir <xliff:g id="APP">%s</xliff:g> en tu perfil personal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"¿Abrir <xliff:g id="APP">%s</xliff:g> en tu perfil de trabajo?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usar navegador personal"</string> diff --git a/java/res/values-eu/strings.xml b/java/res/values-eu/strings.xml index 7570e7dc..e2133c43 100644 --- a/java/res/values-eu/strings.xml +++ b/java/res/values-eu/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Berraktibatu"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ez dago laneko aplikaziorik"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Ez dago aplikazio pertsonalik"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Aplikazio pribaturik ez"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Profil pertsonalean ireki nahi duzu <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Laneko profilean ireki nahi duzu <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Erabili arakatzaile pertsonala"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 66b03cfc..027e71a4 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"لغو مکث"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"برنامه کاریای وجود ندارد"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"برنامه شخصیای وجود ندارد"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"بدون برنامه خصوصی"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> در نمایه شخصی باز شود؟"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> در نمایه کاری باز شود؟"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"استفاده از مرورگر شخصی"</string> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 3b79b195..e079e304 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Jatka käyttöä"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ei työsovelluksia"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Ei henkilökohtaisia sovelluksia"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Ei yksityisiä sovelluksia"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Avataanko <xliff:g id="APP">%s</xliff:g> henkilökohtaisessa profiilissa?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Avataanko <xliff:g id="APP">%s</xliff:g> työprofiilissa?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Käytä henkilökohtaista selainta"</string> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 074ce258..435c0ee6 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"Enregistrer l\'image avec"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"Enregistrer l\'image avec <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"Enregistrer l\'image"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"Utiliser une application différente"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"Utiliser une appli différente"</string> <string name="chooseActivity" msgid="6659724877523973446">"Sélectionner une action"</string> <string name="noApplications" msgid="1139487441772284671">"Aucune application ne peut effectuer cette action."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"Vous utilisez cette application en dehors de votre profil professionnel"</string> @@ -90,7 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Réactiver"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Aucune application professionnelle"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Aucune application personnelle"</string> - <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Aucune application privée"</string> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Aucune appli privée"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Ouvrir <xliff:g id="APP">%s</xliff:g> dans votre profil personnel?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Ouvrir <xliff:g id="APP">%s</xliff:g> dans votre profil professionnel?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Utiliser le navigateur du profil personnel"</string> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index c87644a6..a3a947b5 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -57,7 +57,7 @@ <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{+ # autre fichier}one{+ # autre fichier}many{+ # autres fichiers}other{+ # autres fichiers}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Texte à partager"</string> <string name="sharing_link" msgid="2307694372813942916">"Partager le lien"</string> - <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Partage de l\'image…}one{Partage de # image…}many{Partage de # d\'images…}other{Partage de # images…}}"</string> + <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Partager l\'image…}one{Partager # image…}many{Partager # d\'images…}other{Partager # images…}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Partage de la vidéo…}one{Partage de # vidéo…}many{Partage de # de vidéos…}other{Partage de # vidéos…}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Partage de # fichier}one{Partage de # fichier}many{Partage de # fichiers}other{Partage de # fichiers}}"</string> <string name="sharing_images_with_text" msgid="9005717434461730242">"{count,plural, =1{Partager 1 image avec du texte}one{Partager # image avec du texte}many{Partager # images avec du texte}other{Partager # images avec du texte}}"</string> @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Cette application n\'a pas reçu l\'autorisation d\'enregistrer des contenus audio, mais peut le faire via ce périphérique USB."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Personnel"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Professionnel"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Mode privé"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Privé"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Vue personnelle"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Vue professionnelle"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Affichage en mode privé"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Réactiver"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Aucune appli professionnelle"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Aucune appli personnelle"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Aucune application privée"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Ouvrir <xliff:g id="APP">%s</xliff:g> dans votre profil personnel ?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Ouvrir <xliff:g id="APP">%s</xliff:g> dans votre profil professionnel ?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Utiliser le navigateur personnel"</string> diff --git a/java/res/values-gl/strings.xml b/java/res/values-gl/strings.xml index 6b8a4151..57d518ba 100644 --- a/java/res/values-gl/strings.xml +++ b/java/res/values-gl/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reactivar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Non hai ningunha aplicación do traballo compatible"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Non hai ningunha aplicación persoal compatible"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Non hai ningunha aplicación privada"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Queres abrir <xliff:g id="APP">%s</xliff:g> no teu perfil persoal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Queres abrir <xliff:g id="APP">%s</xliff:g> no teu perfil de traballo?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Utilizar navegador persoal"</string> diff --git a/java/res/values-gu/strings.xml b/java/res/values-gu/strings.xml index d9dd48f4..df459a3d 100644 --- a/java/res/values-gu/strings.xml +++ b/java/res/values-gu/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"આની સાથે છબી કૅપ્ચર કરો"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> વડે છબી કૅપ્ચર કરો"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"છબી કૅપ્ચર કરો"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"અલગ એપ્લિકેશનનો ઉપયોગ કરો"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"અલગ ઍપનો ઉપયોગ કરો"</string> <string name="chooseActivity" msgid="6659724877523973446">"ક્રિયા પસંદ કરો"</string> <string name="noApplications" msgid="1139487441772284671">"કોઈ ઍપ્લિકેશન આ ક્રિયા કરી શકતી નથી."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"તમે તમારી કાર્ય પ્રોફાઇલની બહાર આ એપ્લિકેશનનો ઉપયોગ કરી રહ્યાં છો"</string> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 071b2b54..fb0b8822 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"इस ऐप्लिकेशन को रिकॉर्ड करने की अनुमति नहीं दी गई है. हालांकि, ऐप्लिकेशन इस यूएसबी डिवाइस से ऐसा कर सकता है."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"निजी प्रोफ़ाइल"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"वर्क प्रोफ़ाइल"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"निजी"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"प्राइवेट"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"निजी व्यू"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"वर्क व्यू"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"निजी व्यू"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index ebfe6fe4..2c01f80e 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Ponovno pokreni"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Poslovne aplikacije nisu dostupne"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Osobne aplikacije nisu dostupne"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nema privatnih aplikacija"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Želite li otvoriti aplikaciju <xliff:g id="APP">%s</xliff:g> na osobnom profilu?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Želite li otvoriti aplikaciju <xliff:g id="APP">%s</xliff:g> na poslovnom profilu?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Koristi osobni preglednik"</string> diff --git a/java/res/values-hy/strings.xml b/java/res/values-hy/strings.xml index 8c46bd07..637dedc2 100644 --- a/java/res/values-hy/strings.xml +++ b/java/res/values-hy/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Նորից միացնել"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Աշխատանքային հավելվածներ չկան"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Անձնական հավելվածներ չկան"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Անձնական հավելվածները չեն աջակցում որոշակի բովանդակություն"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Բացե՞լ <xliff:g id="APP">%s</xliff:g> հավելվածը ձեր անձնական պրոֆիլում"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Բացե՞լ <xliff:g id="APP">%s</xliff:g> հավելվածը ձեր աշխատանքային պրոֆիլում"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Օգտագործել անձնական դիտարկիչը"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 02b00466..ca7a32cb 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"Jepret gambar dengan"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"Ambil gambar dengan <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"Jepret gambar"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"Gunakan aplikasi yang berbeda"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"Gunakan aplikasi lain"</string> <string name="chooseActivity" msgid="6659724877523973446">"Pilih tindakan"</string> <string name="noApplications" msgid="1139487441772284671">"Tidak ada apl yang dapat melakukan tindakan ini."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"Anda menggunakan aplikasi ini di luar profil kerja"</string> @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Aplikasi ini tidak diberi izin merekam, tetapi dapat merekam audio melalui perangkat USB ini."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Pribadi"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Kerja"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Pribadi"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Privasi"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Tampilan pribadi"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Tampilan kerja"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Tampilan pribadi"</string> diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml index b6e6e758..3e4e3c4f 100644 --- a/java/res/values-is/strings.xml +++ b/java/res/values-is/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Ljúka hléi"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Engin vinnuforrit"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Engin forrit til einkanota"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Engin einkaforrit"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Opna <xliff:g id="APP">%s</xliff:g> í þínu eigin sniði?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Opna <xliff:g id="APP">%s</xliff:g> í vinnusniðinu þínu?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Nota einkavafra"</string> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index c011fa3c..7118988f 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"A questa app non è stata concessa l\'autorizzazione di registrazione, ma l\'app potrebbe acquisire l\'audio tramite questo dispositivo USB."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Personale"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Lavoro"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Privata"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Privato"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Visualizzazione personale"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Visualizzazione di lavoro"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Visualizzazione privata"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Riattiva"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nessuna app di lavoro"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nessuna app personale"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nessuna app privata"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Aprire <xliff:g id="APP">%s</xliff:g> nel tuo profilo personale?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Aprire <xliff:g id="APP">%s</xliff:g> nel tuo profilo di lavoro?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usa il browser personale"</string> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index c1740360..778482f3 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ביטול ההשהיה"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"אין אפליקציות לעבודה"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"אין אפליקציות לשימוש אישי"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"התוכן הזה לא זמין לאפליקציות פרטיות"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"לפתוח את <xliff:g id="APP">%s</xliff:g> בפרופיל האישי?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"לפתוח את <xliff:g id="APP">%s</xliff:g> בפרופיל העבודה?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"בדפדפן האישי"</string> diff --git a/java/res/values-kk/strings.xml b/java/res/values-kk/strings.xml index 1819fc34..d995ce56 100644 --- a/java/res/values-kk/strings.xml +++ b/java/res/values-kk/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Қайта қосу"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Жұмыс қолданбалары жоқ."</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Жеке қолданбалар жоқ."</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Жеке қолданбалар жоқ."</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> қолданбасын жеке профиліңізде ашу керек пе?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> қолданбасын жұмыс профиліңізде ашу керек пе?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Жеке браузерді пайдалану"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 5e1903af..4e83c8ae 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"일시중지 해제"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"직장 앱 없음"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"개인 앱 없음"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"비공개 앱을 사용할 수 없습니다."</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"개인 프로필에서 <xliff:g id="APP">%s</xliff:g> 앱을 여시겠습니까?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"직장 프로필에서 <xliff:g id="APP">%s</xliff:g> 앱을 여시겠습니까?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"개인 브라우저 사용"</string> diff --git a/java/res/values-ky/strings.xml b/java/res/values-ky/strings.xml index 56915f4b..883083b8 100644 --- a/java/res/values-ky/strings.xml +++ b/java/res/values-ky/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Иштетүү"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Жумуш колдонмолору жок"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Жеке колдонмолор жок"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Жеке колдонмолор жок"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> колдонмосу жеке профилде ачылсынбы?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> колдонмосу жумуш профилинде ачылсынбы?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Жеке серепчини колдонуу"</string> diff --git a/java/res/values-lo/strings.xml b/java/res/values-lo/strings.xml index 314a3b05..6f13fa7a 100644 --- a/java/res/values-lo/strings.xml +++ b/java/res/values-lo/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"ບັນທຶກຮູບພາບດ້ວຍ"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"ບັນທຶກຮູບພາບດ້ວຍ <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"ບັນທຶກຮູບພາບ"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"ນຳໃຊ້ແອັບຯອື່ນ"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"ໃຊ້ແອັບອື່ນ"</string> <string name="chooseActivity" msgid="6659724877523973446">"ເລືອກຄຳສັ່ງ"</string> <string name="noApplications" msgid="1139487441772284671">"ບໍ່ມີແອັບຯໃດສາມາດເຮັດວຽກນີ້ໄດ້."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"ທ່ານກຳລັງໃຊ້ແອັບຯນີ້ນອກໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index e405b66a..0ce3f84b 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Aktivizēt"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nav darba lietotņu"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nav personīgu lietotņu"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nav privātu lietotņu"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Vai atvērt lietotni <xliff:g id="APP">%s</xliff:g> jūsu personīgajā profilā?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Vai atvērt lietotni <xliff:g id="APP">%s</xliff:g> jūsu darba profilā?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Izmantot personīgo pārlūku"</string> diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml index df46dc98..ab2ac0d0 100644 --- a/java/res/values-mk/strings.xml +++ b/java/res/values-mk/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"Сними слика со"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"Снимање слика со <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"Сними слика"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"Користи различна апликација"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"Употреби друга апликација"</string> <string name="chooseActivity" msgid="6659724877523973446">"Избирање дејство"</string> <string name="noApplications" msgid="1139487441772284671">"Нема апликации што можат да го извршат ова дејство."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"Ја користите апликацијата надвор од работниот профил"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Прекини ја паузата"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Нема работни апликации"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Нема лични апликации"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Нема приватни апликации"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Да се отвори <xliff:g id="APP">%s</xliff:g> во личниот профил?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Да се отвори <xliff:g id="APP">%s</xliff:g> во работниот профил?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Користи личен прелистувач"</string> diff --git a/java/res/values-ml/strings.xml b/java/res/values-ml/strings.xml index 90eb4bf7..640a0fe0 100644 --- a/java/res/values-ml/strings.xml +++ b/java/res/values-ml/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"ഇനിപ്പറയുന്നതിൽ ചിത്രം എടുക്കുക:"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> ഉപയോഗിച്ച് ചിത്രം എടുക്കുക"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"ചിത്രം എടുക്കുക"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"മറ്റൊരു അപ്ലിക്കേഷൻ ഉപയോഗിക്കുക"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"മറ്റൊരു ആപ്പ് ഉപയോഗിക്കുക"</string> <string name="chooseActivity" msgid="6659724877523973446">"ഒരു പ്രവർത്തനം തിരഞ്ഞെടുക്കുക"</string> <string name="noApplications" msgid="1139487441772284671">"അപ്ലിക്കേഷനുകൾക്കൊന്നും ഈ പ്രവർത്തനം നിർവഹിക്കാനാവില്ല."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിന് പുറത്ത് ഈ അപ്ലിക്കേഷൻ ഉപയോഗിക്കുന്നു"</string> diff --git a/java/res/values-mn/strings.xml b/java/res/values-mn/strings.xml index 469afa50..b19387f1 100644 --- a/java/res/values-mn/strings.xml +++ b/java/res/values-mn/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Үргэлжлүүлэх"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ямар ч ажлын апп байхгүй байна"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Ямар ч хувийн апп байхгүй байна"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Ямар ч хувийн апп байхгүй"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Хувийн профайл дээрээ <xliff:g id="APP">%s</xliff:g>-г нээх үү?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Ажлын профайл дээрээ <xliff:g id="APP">%s</xliff:g>-г нээх үү?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Хувийн хөтөч ашиглах"</string> diff --git a/java/res/values-mr/strings.xml b/java/res/values-mr/strings.xml index c4c0818c..b9a3a349 100644 --- a/java/res/values-mr/strings.xml +++ b/java/res/values-mr/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"यासह इमेज कॅप्चर करा"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> वापरून इमेज कॅप्चर करा"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"इमेज कॅप्चर करा"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"एक भिन्न अॅप वापरा"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"वेगळे अॅप वापरा"</string> <string name="chooseActivity" msgid="6659724877523973446">"कृती निवडा"</string> <string name="noApplications" msgid="1139487441772284671">"कोणतेही अॅप्स ही क्रिया करू शकत नाहीत."</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"तुम्ही हा अॅप आपल्या कार्य प्रोफाईलच्या बाहेर वापरत आहात"</string> diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index b5e99492..e9c7958e 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Apl ini belum diberikan kebenaran merakam tetapi dapat merakam audio melalui peranti USB ini."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Peribadi"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Kerja"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Peribadi"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Persendirian"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Paparan peribadi"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Paparan kerja"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Paparan peribadi"</string> diff --git a/java/res/values-my/strings.xml b/java/res/values-my/strings.xml index 475a755f..545810a6 100644 --- a/java/res/values-my/strings.xml +++ b/java/res/values-my/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ပြန်ဖွင့်ရန်"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"အလုပ်သုံးအက်ပ်များ မရှိပါ"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"ကိုယ်ပိုင်အက်ပ်များ မရှိပါ"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"သီးသန့်အက်ပ် မရှိပါ"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> ကို သင့်ကိုယ်ပိုင်ပရိုဖိုင်တွင် ဖွင့်မလား။"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> ကို သင့်အလုပ်ပရိုဖိုင်တွင် ဖွင့်မလား။"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ကိုယ်ပိုင်ဘရောင်ဇာ သုံးရန်"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index e455a2b6..4923fff9 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Slå av pausen"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ingen jobbapper"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Ingen personlige apper"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Ingen private apper"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Vil du åpne <xliff:g id="APP">%s</xliff:g> i den personlige profilen din?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Vil du åpne <xliff:g id="APP">%s</xliff:g> i jobbprofilen din?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Bruk den personlige nettleseren"</string> diff --git a/java/res/values-ne/strings.xml b/java/res/values-ne/strings.xml index 614ecfe5..cd213e84 100644 --- a/java/res/values-ne/strings.xml +++ b/java/res/values-ne/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"यस मार्फत छविलाई कैंद गर्नुहोस्"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> मार्फत फोटो खिच्नुहोस्"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"छविलाई कैंद गर्नुहोस्"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"फरक एप प्रयोग गर्नुहोस्"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"अर्को एप प्रयोग गर्नुहोस्"</string> <string name="chooseActivity" msgid="6659724877523973446">"कारबाही चयन गर्नुहोस्"</string> <string name="noApplications" msgid="1139487441772284671">"कुनै पनि एपहरूले यो कार्य गर्न सक्दैनन्।"</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"तपाईं तपाईंको कार्य प्रोफाइल बाहिर यो एप प्रयोग गरिरहनु भएको छ"</string> diff --git a/java/res/values-or/strings.xml b/java/res/values-or/strings.xml index 9d36c473..c2536e42 100644 --- a/java/res/values-or/strings.xml +++ b/java/res/values-or/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"କୌଣସି ୱାର୍କ ଆପ୍ ନାହିଁ"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"କୌଣସି ବ୍ୟକ୍ତିଗତ ଆପ୍ ନାହିଁ"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"କୌଣସି ପ୍ରାଇଭେଟ ଆପ୍ସ ନାହିଁ"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g>କୁ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲରେ ଖୋଲିବେ?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g>କୁ ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରେ ଖୋଲିବେ?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ବ୍ୟକ୍ତିଗତ ବ୍ରାଉଜର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> diff --git a/java/res/values-pa/strings.xml b/java/res/values-pa/strings.xml index 60a9c0f5..fbf6fa6a 100644 --- a/java/res/values-pa/strings.xml +++ b/java/res/values-pa/strings.xml @@ -42,7 +42,7 @@ <string name="whichImageCaptureApplication" msgid="7830965894804399333">"ਇਸ ਨਾਲ ਚਿਤਰ ਕੈਪਚਰ ਕਰੋ"</string> <string name="whichImageCaptureApplicationNamed" msgid="5927801386307049780">"<xliff:g id="APP">%1$s</xliff:g> ਨਾਲ ਚਿੱਤਰ ਨੂੰ ਕੈਪਚਰ ਕਰੋ"</string> <string name="whichImageCaptureApplicationLabel" msgid="987153638235357094">"ਚਿਤਰ ਕੈਪਚਰ ਕਰੋ"</string> - <string name="use_a_different_app" msgid="2062380818535918975">"ਇੱਕ ਵੱਖਰਾ ਖਾਤਾ ਵਰਤੋ"</string> + <string name="use_a_different_app" msgid="2062380818535918975">"ਕੋਈ ਵੱਖਰੀ ਐਪ ਵਰਤੋ"</string> <string name="chooseActivity" msgid="6659724877523973446">"ਕਾਰਵਾਈ ਚੁਣੋ"</string> <string name="noApplications" msgid="1139487441772284671">"ਕੋਈ ਐਪਾਂ ਇਸ ਕਾਰਵਾਈ ਨੂੰ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ।"</string> <string name="forward_intent_to_owner" msgid="6454987608971162379">"ਤੁਸੀਂ ਇਹ ਐਪ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦੇ ਬਾਹਰ ਵਰਤ ਰਹੇ ਹੋ"</string> @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"ਇਸ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਪਰ ਇਹ USB ਡੀਵਾਈਸ ਰਾਹੀਂ ਆਡੀਓ ਕੈਪਚਰ ਕਰ ਸਕਦੀ ਹੈ।"</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"ਨਿੱਜੀ"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"ਕੰਮ ਸੰਬੰਧੀ"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"ਨਿੱਜੀ"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"ਪ੍ਰਾਈਵੇਟ"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"ਵਿਅਕਤੀਗਤ ਦ੍ਰਿਸ਼"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"ਕਾਰਜ ਦ੍ਰਿਸ਼"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"ਨਿੱਜੀ ਦ੍ਰਿਸ਼"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ਰੋਕ ਹਟਾਓ"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"ਕੋਈ ਕੰਮ ਸੰਬੰਧੀ ਐਪ ਨਹੀਂ"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"ਕੋਈ ਨਿੱਜੀ ਐਪ ਨਹੀਂ"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"ਕੋਈ ਨਿੱਜੀ ਐਪ ਨਹੀਂ"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"ਕੀ ਆਪਣੇ ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ <xliff:g id="APP">%s</xliff:g> ਨੂੰ ਖੋਲ੍ਹਣਾ ਹੈ?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"ਕੀ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ <xliff:g id="APP">%s</xliff:g> ਨੂੰ ਖੋਲ੍ਹਣਾ ਹੈ?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ਨਿੱਜੀ ਬ੍ਰਾਊਜ਼ਰ ਵਰਤੋ"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 48c1ca28..f1190953 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Ta aplikacja nie ma uprawnień do nagrywania, ale może rejestrować dźwięk za pomocą tego urządzenia USB."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Osobiste"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Służbowe"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Prywatna"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Prywatne"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Widok osobisty"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Widok służbowy"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Widok prywatny"</string> diff --git a/java/res/values-pt-rBR/strings.xml b/java/res/values-pt-rBR/strings.xml index 665de8b6..c049deec 100644 --- a/java/res/values-pt-rBR/strings.xml +++ b/java/res/values-pt-rBR/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reativar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nenhum app de trabalho"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nenhum app pessoal"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Sem apps particulares"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Abrir o app <xliff:g id="APP">%s</xliff:g> no seu perfil pessoal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Abrir o app <xliff:g id="APP">%s</xliff:g> no seu perfil de trabalho?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usar o navegador pessoal"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 08694c9d..aba995da 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Retomar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Sem apps de trabalho"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Sem apps pessoais"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nenhuma app privada"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Abrir a app <xliff:g id="APP">%s</xliff:g> no seu perfil pessoal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Abrir a app <xliff:g id="APP">%s</xliff:g> no seu perfil de trabalho?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usar navegador pessoal"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index 665de8b6..c049deec 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reativar"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nenhum app de trabalho"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nenhum app pessoal"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Sem apps particulares"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Abrir o app <xliff:g id="APP">%s</xliff:g> no seu perfil pessoal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Abrir o app <xliff:g id="APP">%s</xliff:g> no seu perfil de trabalho?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Usar o navegador pessoal"</string> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 8620e2a5..0acb22d2 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Reactivează"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nicio aplicație pentru lucru"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nicio aplicație personală"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nu există aplicații private"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul personal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul de serviciu?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Folosește browserul personal"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index ca852709..995abf02 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -77,10 +77,10 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Приложению не разрешено записывать звук, однако оно может делать это с помощью этого USB-устройства."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Личное"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Рабочее"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Личное пространство"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Частное пространство"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Просмотр личных данных"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Просмотр рабочих данных"</string> - <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Личное пространство"</string> + <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Частное пространство"</string> <string name="resolver_cross_profile_blocked" msgid="3515194063758605377">"Заблокировано вашим администратором"</string> <string name="resolver_cant_share_with_work_apps_explanation" msgid="2984105853145456723">"Этим контентом нельзя делиться с рабочими приложениями."</string> <string name="resolver_cant_access_work_apps_explanation" msgid="1463093773348988122">"Этот контент нельзя открыть в рабочем приложении."</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Включить"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Не поддерживается рабочими приложениями."</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Не поддерживается личными приложениями."</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Частные приложения не поддерживаются."</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Открыть приложение \"<xliff:g id="APP">%s</xliff:g>\" в личном профиле?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Открыть приложение \"<xliff:g id="APP">%s</xliff:g>\" в рабочем профиле?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Использовать личный браузер"</string> diff --git a/java/res/values-sq/strings.xml b/java/res/values-sq/strings.xml index 8043a15c..e18aaea1 100644 --- a/java/res/values-sq/strings.xml +++ b/java/res/values-sq/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Hiq nga pauza"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Nuk ka aplikacione pune"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Nuk ka aplikacione personale"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Nuk ka aplikacione private"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Të hapet <xliff:g id="APP">%s</xliff:g> në profilin tënd personal?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Të hapet <xliff:g id="APP">%s</xliff:g> në profilin tënd të punës?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Përdor shfletuesin personal"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 0359c894..0f44b233 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -68,7 +68,7 @@ <string name="sharing_files_with_link" msgid="6052797122358827239">"{count,plural, =1{Дели се фајл са линком}one{Дели се # фајл са линком}few{Деле се # фајла са линком}other{Дели се # фајлова са линком}}"</string> <string name="sharing_album" msgid="191743129899503345">"Дељени албум"</string> <string name="sharing_images_only" msgid="7762589767189955438">"{count,plural, =1{Само слика}one{Само слике}few{Само слике}other{Само слике}}"</string> - <string name="sharing_videos_only" msgid="5549729252364968606">"{count,plural, =1{Само видео}one{Само видео снимци}few{Само видео снимци}other{Само видео снимци}}"</string> + <string name="sharing_videos_only" msgid="5549729252364968606">"{count,plural, =1{Само видео}one{Само видеи}few{Само видеи}other{Само видеи}}"</string> <string name="sharing_files_only" msgid="6603666533766964768">"{count,plural, =1{Само фајл}one{Само фајлови}few{Само фајлови}other{Само фајлови}}"</string> <string name="image_preview_a11y_description" msgid="297102643932491797">"Сличица за преглед слике"</string> <string name="video_preview_a11y_description" msgid="683440858811095990">"Сличица за преглед видеа"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Поново активирај"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Нема пословних апликација"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Нема личних апликација"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Без приватних апликација"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Желите да на личном профилу отворите: <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Желите да на пословном профилу отворите: <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Користи лични прегледач"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index a459f69c..c62a7a8f 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Återuppta"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Inga jobbappar"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Inga privata appar"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Inga privata appar"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Vill du öppna <xliff:g id="APP">%s</xliff:g> i din privata profil?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Vill du öppna <xliff:g id="APP">%s</xliff:g> i din jobbprofil?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Använd privat webbläsare"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 63dabd19..7d2206c1 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -77,7 +77,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Programu hii haijapewa ruhusa ya kurekodi lakini inaweza kurekodi sauti kupitia kifaa hiki cha USB."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Binafsi"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Kazini"</string> - <string name="resolver_private_tab" msgid="3707548826254095157">"Wa faragha"</string> + <string name="resolver_private_tab" msgid="3707548826254095157">"Faragha"</string> <string name="resolver_personal_tab_accessibility" msgid="4467784352232582574">"Mwonekano wa binafsi"</string> <string name="resolver_work_tab_accessibility" msgid="7581878836587799920">"Mwonekano wa kazini"</string> <string name="resolver_private_tab_accessibility" msgid="2513122834337197252">"Mwonekano wa faragha"</string> @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Acha kusitisha"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Hakuna programu za kazini"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Hakuna programu za binafsi"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Hakuna programu za faragha"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Je, unataka kufungua <xliff:g id="APP">%s</xliff:g> katika wasifu wako binafsi?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Je, unataka kufungua <xliff:g id="APP">%s</xliff:g> katika wasifu wako wa kazi?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Tumia kivinjari cha binafsi"</string> diff --git a/java/res/values-ta/strings.xml b/java/res/values-ta/strings.xml index dcddcf0c..a83be54f 100644 --- a/java/res/values-ta/strings.xml +++ b/java/res/values-ta/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"மீண்டும் இயக்கு"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"பணி ஆப்ஸ் எதுவுமில்லை"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"தனிப்பட்ட ஆப்ஸ் எதுவுமில்லை"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"தனிப்பட்ட ஆப்ஸ் எதுவுமில்லை"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"உங்கள் தனிப்பட்ட கணக்கில் <xliff:g id="APP">%s</xliff:g> ஆப்ஸைத் திறக்கவா?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"உங்கள் பணிக் கணக்கில் <xliff:g id="APP">%s</xliff:g> ஆப்ஸைத் திறக்கவா?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"தனிப்பட்ட உலாவியைப் பயன்படுத்து"</string> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 2deef229..7b3a4026 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"ยกเลิกการหยุดชั่วคราว"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"ไม่มีแอปงาน"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"ไม่มีแอปส่วนตัว"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"ไม่มีแอปส่วนตัว"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"เปิด <xliff:g id="APP">%s</xliff:g> ในโปรไฟล์ส่วนตัวไหม"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"เปิด <xliff:g id="APP">%s</xliff:g> ในโปรไฟล์งานไหม"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"ใช้เบราว์เซอร์ส่วนตัว"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index ccf43d7b..cdbe4dcb 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"I-unpause"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Walang app para sa trabaho"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Walang personal na app"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Walang pribadong app"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Buksan ang <xliff:g id="APP">%s</xliff:g> sa iyong personal na profile?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Buksan ang <xliff:g id="APP">%s</xliff:g> sa iyong profile sa trabaho?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Gamitin ang personal na browser"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index e671cf89..502b2538 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Devam ettir"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"İş uygulaması yok"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Kişisel uygulama yok"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Özel uygulama yok"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> uygulaması kişisel profilinizde açılsın mı?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> uygulaması iş profilinizde açılsın mı?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Kişisel tarayıcıyı kullan"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 90ca8213..9d29dbb5 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Увімкнути знову"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Немає робочих додатків"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Немає особистих додатків"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Немає приватних додатків"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Відкрити додаток <xliff:g id="APP">%s</xliff:g> в особистому профілі?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Відкрити додаток <xliff:g id="APP">%s</xliff:g> у робочому профілі?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Використати особистий веб-переглядач"</string> diff --git a/java/res/values-uz/strings.xml b/java/res/values-uz/strings.xml index 482f0a90..b59303da 100644 --- a/java/res/values-uz/strings.xml +++ b/java/res/values-uz/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Davom ettirish"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Ishga oid ilovalar topilmadi"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Shaxsiy ilovalar topilmadi"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Shaxsiy ilovalar ishlamaydi"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"<xliff:g id="APP">%s</xliff:g> shaxsiy profilda ochilsinmi?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"<xliff:g id="APP">%s</xliff:g> shaxsiy profilda ochilsinmi?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Shaxsiy brauzerdan foydalanish"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index beacc185..da23612f 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"Tiếp tục"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"Không có ứng dụng công việc"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"Không có ứng dụng cá nhân"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"Không có ứng dụng riêng tư nào"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"Mở <xliff:g id="APP">%s</xliff:g> trong hồ sơ cá nhân của bạn?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"Mở <xliff:g id="APP">%s</xliff:g> trong hồ sơ công việc của bạn?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"Dùng trình duyệt cá nhân"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index afe104b4..0adbaf61 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"解除暂停"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"没有支持该内容的工作应用"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"没有支持该内容的个人应用"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"无可用的专用应用"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"要使用个人资料打开 <xliff:g id="APP">%s</xliff:g> 吗?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"要使用工作资料打开 <xliff:g id="APP">%s</xliff:g> 吗?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"使用个人浏览器"</string> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index e65b6dc8..c6c0afc2 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"取消暫停"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"沒有適用的工作應用程式"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"沒有適用的個人應用程式"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"沒有私人應用程式"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"要在個人設定檔中開啟「<xliff:g id="APP">%s</xliff:g>」嗎?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"要在工作設定檔中開啟「<xliff:g id="APP">%s</xliff:g>」嗎?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"使用個人瀏覽器"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index f90ef68b..22c6455b 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -90,8 +90,7 @@ <string name="resolver_switch_on_work" msgid="8678893259344318807">"取消暫停"</string> <string name="resolver_no_work_apps_available" msgid="6139818641313189903">"沒有適用的工作應用程式"</string> <string name="resolver_no_personal_apps_available" msgid="8479033344701050767">"沒有適用的個人應用程式"</string> - <!-- no translation found for resolver_no_private_apps_available (4164473548027417456) --> - <skip /> + <string name="resolver_no_private_apps_available" msgid="4164473548027417456">"私人應用程式不支援這項功能"</string> <string name="miniresolver_open_in_personal" msgid="8397377137465016575">"要在個人資料夾中開啟「<xliff:g id="APP">%s</xliff:g>」嗎?"</string> <string name="miniresolver_open_in_work" msgid="4271638122142624693">"要在工作資料夾中開啟「<xliff:g id="APP">%s</xliff:g>」嗎?"</string> <string name="miniresolver_use_personal_browser" msgid="1428911732509069292">"使用個人瀏覽器"</string> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 17a514d7..32c61327 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -284,6 +284,12 @@ <!-- Error message. This message lets the user know that their IT admin doesn't allow them to open this specific content with an app in their personal profile. [CHAR LIMIT=NONE] --> <string name="resolver_cant_access_personal_apps_explanation">This content can\u2019t be opened with personal apps</string> + <!-- Error message. This text is explaining that the user's IT admin doesn't allow this specific content to be shared with apps in the private profile. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_share_with_private_apps_explanation">This content can\u2019t be shared with private apps</string> + + <!-- Error message. This message lets the user know that their IT admin doesn't allow them to open this specific content with an app in their private profile. [CHAR LIMIT=NONE] --> + <string name="resolver_cant_access_private_apps_explanation">This content can\u2019t be opened with private apps</string> + <!-- Error message. This text lets the user know that they need to turn on work apps in order to share or open content. There's also a button a user can tap to turn on the apps. [CHAR LIMIT=NONE] --> <string name="resolver_turn_on_work_apps">Work apps are paused</string> <!-- Button text. This button unpauses a user's work apps and data. [CHAR LIMIT=NONE] --> diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index f01fd77c..8b78fc7e 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -17,14 +17,7 @@ package com.android.intentresolver; import static android.app.VoiceInteractor.PickOptionRequest.Option; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; -import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static androidx.lifecycle.LifecycleKt.getCoroutineScope; @@ -111,13 +104,12 @@ import com.android.intentresolver.data.repository.DevicePolicyResources; import com.android.intentresolver.domain.interactor.UserInteractor; import com.android.intentresolver.emptystate.CompositeEmptyStateProvider; import com.android.intentresolver.emptystate.CrossProfileIntentsChecker; -import com.android.intentresolver.emptystate.DevicePolicyBlockerEmptyState; -import com.android.intentresolver.emptystate.EmptyState; import com.android.intentresolver.emptystate.EmptyStateProvider; import com.android.intentresolver.emptystate.NoAppsAvailableEmptyStateProvider; import com.android.intentresolver.emptystate.NoCrossProfileEmptyStateProvider; import com.android.intentresolver.emptystate.WorkProfilePausedEmptyStateProvider; import com.android.intentresolver.grid.ChooserGridAdapter; +import com.android.intentresolver.icons.Caching; import com.android.intentresolver.icons.TargetDataLoader; import com.android.intentresolver.inject.Background; import com.android.intentresolver.logging.EventLog; @@ -175,6 +167,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import javax.inject.Inject; +import javax.inject.Provider; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -213,7 +206,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements private static final String TAB_TAG_WORK = "work"; private static final String LAST_SHOWN_TAB_KEY = "last_shown_tab_key"; - protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser"; + public static final String METRICS_CATEGORY_CHOOSER = "intent_chooser"; private int mLayoutId; private UserHandle mHeaderCreatorUser; @@ -265,7 +258,11 @@ public class ChooserActivity extends Hilt_ChooserActivity implements @Inject @AppPredictionAvailable public boolean mAppPredictionAvailable; @Inject @ImageEditor public Optional<ComponentName> mImageEditor; @Inject @NearbyShare public Optional<ComponentName> mNearbyShare; - @Inject public TargetDataLoader mTargetDataLoader; + protected TargetDataLoader mTargetDataLoader; + @Inject public Provider<TargetDataLoader> mTargetDataLoaderProvider; + @Inject + @Caching + public Provider<TargetDataLoader> mCachingTargetDataLoaderProvider; @Inject public DevicePolicyResources mDevicePolicyResources; @Inject public ProfilePagerResources mProfilePagerResources; @Inject public PackageManager mPackageManager; @@ -340,6 +337,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements super.onCreate(savedInstanceState); Log.i(TAG, "onCreate"); + mTargetDataLoader = mChooserServiceFeatureFlags.chooserPayloadToggling() + ? mCachingTargetDataLoaderProvider.get() + : mTargetDataLoaderProvider.get(); + setTheme(R.style.Theme_DeviceDefault_Chooser); // Initializer is invoked when this function returns, via Lifecycle. @@ -785,6 +786,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements mRequest.getInitialIntents(), mMaxTargetsPerRow); mChooserMultiProfilePagerAdapter.setCurrentPage(currentPage); + for (int i = 0, count = mChooserMultiProfilePagerAdapter.getItemCount(); i < count; i++) { + mChooserMultiProfilePagerAdapter.getPageAdapterForIndex(i) + .getListAdapter().setAnimateItems(false); + } if (mPersonalPackageMonitor != null) { mPersonalPackageMonitor.unregister(); } @@ -1393,17 +1398,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements return context.getSharedPreferences(PINNED_SHARED_PREFS_NAME, MODE_PRIVATE); } - protected ChooserMultiProfilePagerAdapter createMultiProfilePagerAdapter() { - return createMultiProfilePagerAdapter( - /* context = */ this, - mProfilePagerResources, - mViewModel.getRequest().getValue(), - mProfiles, - mProfileAvailability, - mRequest.getInitialIntents(), - mMaxTargetsPerRow); - } - private ChooserMultiProfilePagerAdapter createMultiProfilePagerAdapter( Context context, ProfilePagerResources profilePagerResources, @@ -1459,39 +1453,11 @@ public class ChooserActivity extends Hilt_ChooserActivity implements } protected EmptyStateProvider createBlockerEmptyStateProvider() { - final boolean isSendAction = mRequest.isSendActionTarget(); - - final EmptyState noWorkToPersonalEmptyState = - new DevicePolicyBlockerEmptyState( - /* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ - isSendAction ? RESOLVER_CANT_SHARE_WITH_PERSONAL : RESOLVER_CANT_ACCESS_PERSONAL, - /* defaultSubtitleResource= */ - isSendAction ? R.string.resolver_cant_share_with_personal_apps_explanation - : R.string.resolver_cant_access_personal_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL, - /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER); - - final EmptyState noPersonalToWorkEmptyState = - new DevicePolicyBlockerEmptyState( - /* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ - isSendAction ? RESOLVER_CANT_SHARE_WITH_WORK : RESOLVER_CANT_ACCESS_WORK, - /* defaultSubtitleResource= */ - isSendAction ? R.string.resolver_cant_share_with_work_apps_explanation - : R.string.resolver_cant_access_work_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK, - /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER); - return new NoCrossProfileEmptyStateProvider( mProfiles, - noWorkToPersonalEmptyState, - noPersonalToWorkEmptyState, - createCrossProfileIntentsChecker()); + mDevicePolicyResources, + createCrossProfileIntentsChecker(), + mRequest.isSendActionTarget()); } private int findSelectedProfile() { diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index 29b5698b..8b848e55 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -153,6 +153,8 @@ public class ChooserListAdapter extends ResolverListAdapter { } }; + private boolean mAnimateItems = true; + public ChooserListAdapter( Context context, List<Intent> payloadIntents, @@ -308,6 +310,10 @@ public class ChooserListAdapter extends ResolverListAdapter { } } + public void setAnimateItems(boolean animateItems) { + mAnimateItems = animateItems; + } + @Override public void handlePackagesChanged() { if (mPackageChangeCallback != null) { @@ -371,18 +377,15 @@ public class ChooserListAdapter extends ResolverListAdapter { final CharSequence displayLabel = Objects.requireNonNullElse(info.getDisplayLabel(), ""); final CharSequence extendedInfo = Objects.requireNonNullElse(info.getExtendedInfo(), ""); holder.bindLabel(displayLabel, extendedInfo); - if (!TextUtils.isEmpty(displayLabel)) { + if (mAnimateItems && !TextUtils.isEmpty(displayLabel)) { mAnimationTracker.animateLabel(holder.text, info); } - if (!TextUtils.isEmpty(extendedInfo) && holder.text2.getVisibility() == View.VISIBLE) { + if (mAnimateItems + && !TextUtils.isEmpty(extendedInfo) + && holder.text2.getVisibility() == View.VISIBLE) { mAnimationTracker.animateLabel(holder.text2, info); } - holder.bindIcon(info); - if (info.hasDisplayIcon()) { - mAnimationTracker.animateIcon(holder.icon, info); - } - if (info.isSelectableTargetInfo()) { // direct share targets should append the application name for a better readout DisplayResolveInfo rInfo = info.getDisplayResolveInfo(); @@ -418,6 +421,11 @@ public class ChooserListAdapter extends ResolverListAdapter { } } + holder.bindIcon(info); + if (mAnimateItems && info.hasDisplayIcon()) { + mAnimationTracker.animateIcon(holder.icon, info); + } + if (info.isPlaceHolderTargetInfo()) { bindPlaceholder(holder); } diff --git a/java/src/com/android/intentresolver/ProfileHelper.kt b/java/src/com/android/intentresolver/ProfileHelper.kt index e1d912c3..53a873a3 100644 --- a/java/src/com/android/intentresolver/ProfileHelper.kt +++ b/java/src/com/android/intentresolver/ProfileHelper.kt @@ -80,12 +80,12 @@ constructor( launchedByUser.handle } - fun findProfileType(handle: UserHandle): Profile.Type? { - val matched = - profiles.firstOrNull { it.primary.handle == handle || it.clone?.handle == handle } - return matched?.type + fun findProfile(handle: UserHandle): Profile? { + return profiles.firstOrNull { it.primary.handle == handle || it.clone?.handle == handle } } + fun findProfileType(handle: UserHandle): Profile.Type? = findProfile(handle)?.type + // Name retained for ease of review, to be renamed later fun getQueryIntentsHandle(handle: UserHandle): UserHandle? { return if (isLaunchedAsCloneProfile && handle == personalHandle) { diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index 1b08d957..a402fc72 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -16,12 +16,7 @@ package com.android.intentresolver; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK; -import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; -import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static androidx.lifecycle.LifecycleKt.getCoroutineScope; @@ -94,8 +89,6 @@ import com.android.intentresolver.data.repository.DevicePolicyResources; import com.android.intentresolver.domain.interactor.UserInteractor; import com.android.intentresolver.emptystate.CompositeEmptyStateProvider; import com.android.intentresolver.emptystate.CrossProfileIntentsChecker; -import com.android.intentresolver.emptystate.DevicePolicyBlockerEmptyState; -import com.android.intentresolver.emptystate.EmptyState; import com.android.intentresolver.emptystate.EmptyStateProvider; import com.android.intentresolver.emptystate.NoAppsAvailableEmptyStateProvider; import com.android.intentresolver.emptystate.NoCrossProfileEmptyStateProvider; @@ -184,7 +177,6 @@ public class ResolverActivity extends Hilt_ResolverActivity implements private Space mFooterSpacer = null; protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver"; - protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser"; /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */ private final boolean mWorkProfileHasBeenEnabled = false; @@ -449,42 +441,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements } protected EmptyStateProvider createBlockerEmptyStateProvider() { - final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser()); + boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser()); if (!shouldShowNoCrossProfileIntentsEmptyState) { // Implementation that doesn't show any blockers return new EmptyStateProvider() {}; } - - final EmptyState noWorkToPersonalEmptyState = - new DevicePolicyBlockerEmptyState( - /* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL, - /* defaultSubtitleResource= */ - R.string.resolver_cant_access_personal_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL, - /* devicePolicyEventCategory= */ - ResolverActivity.METRICS_CATEGORY_RESOLVER); - - final EmptyState noPersonalToWorkEmptyState = - new DevicePolicyBlockerEmptyState( - /* context= */ this, - /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, - /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked, - /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK, - /* defaultSubtitleResource= */ - R.string.resolver_cant_access_work_apps_explanation, - /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK, - /* devicePolicyEventCategory= */ - ResolverActivity.METRICS_CATEGORY_RESOLVER); - return new NoCrossProfileEmptyStateProvider( mProfiles, - noWorkToPersonalEmptyState, - noPersonalToWorkEmptyState, - createCrossProfileIntentsChecker()); + mDevicePolicyResources, + createCrossProfileIntentsChecker(), + /* isShare= */ false); } /** @@ -1390,7 +1357,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements // Load the icon asynchronously ImageView icon = findViewById(com.android.internal.R.id.icon); - targetDataLoader.loadAppTargetIcon( + targetDataLoader.getOrLoadAppTargetIcon( otherProfileResolveInfo, inactiveAdapter.getUserHandle(), (drawable) -> { diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index 2a8fcfa4..5fd37d43 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -739,26 +739,31 @@ public class ResolverListAdapter extends BaseAdapter { holder.bindLabel("", ""); loadLabel(dri); } - holder.bindIcon(info); if (!dri.hasDisplayIcon()) { loadIcon(dri); } + holder.bindIcon(info); } } protected final void loadIcon(DisplayResolveInfo info) { if (mRequestedIcons.add(info)) { - mTargetDataLoader.loadAppTargetIcon( + Drawable icon = mTargetDataLoader.getOrLoadAppTargetIcon( info, getUserHandle(), - (drawable) -> onIconLoaded(info, drawable)); + (drawable) -> { + onIconLoaded(info, drawable); + notifyDataSetChanged(); + }); + if (icon != null) { + onIconLoaded(info, icon); + } } } private void onIconLoaded(DisplayResolveInfo displayResolveInfo, Drawable drawable) { if (!displayResolveInfo.hasDisplayIcon()) { displayResolveInfo.getDisplayIconHolder().setDisplayIcon(drawable); - notifyDataSetChanged(); } } @@ -822,7 +827,7 @@ public class ResolverListAdapter extends BaseAdapter { public void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) { final DisplayResolveInfo iconInfo = getFilteredItem(); if (iconInfo != null) { - mTargetDataLoader.loadAppTargetIcon( + mTargetDataLoader.getOrLoadAppTargetIcon( iconInfo, getUserHandle(), iconView::setImageDrawable); } } diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index 536f11ce..5e44c53e 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -196,6 +196,7 @@ public class DisplayResolveInfo implements TargetInfo { } @Override + @NonNull public ComponentName getResolvedComponentName() { return new ComponentName(mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name); diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java index 4fe28384..95cb443e 100644 --- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.UserHandle; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.ArrayList; @@ -123,6 +124,7 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { } @Override + @NonNull public ComponentName getResolvedComponentName() { if (hasSelected()) { return mTargetInfos.get(mSelected).getResolvedComponentName(); diff --git a/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt b/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt index 75faa068..eb35a358 100644 --- a/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt +++ b/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt @@ -27,13 +27,15 @@ import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFIL import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY import android.content.res.Resources +import androidx.annotation.OpenForTesting import com.android.intentresolver.R import com.android.intentresolver.inject.ApplicationOwned import javax.inject.Inject import javax.inject.Singleton +@OpenForTesting @Singleton -class DevicePolicyResources +open class DevicePolicyResources @Inject constructor( @ApplicationOwned private val resources: Resources, @@ -102,7 +104,7 @@ constructor( ) } - val crossProfileBlocked by lazy { + open val crossProfileBlocked by lazy { requireNotNull( policyResources.getString(RESOLVER_CROSS_PROFILE_BLOCKED_TITLE) { resources.getString(R.string.resolver_cross_profile_blocked) @@ -110,22 +112,30 @@ constructor( ) } - fun toPersonalBlockedByPolicyMessage(sendAction: Boolean): String { - return if (sendAction) { + open fun toPersonalBlockedByPolicyMessage(share: Boolean): String { + return if (share) { resources.getString(R.string.resolver_cant_share_with_personal_apps_explanation) } else { resources.getString(R.string.resolver_cant_access_personal_apps_explanation) } } - fun toWorkBlockedByPolicyMessage(sendAction: Boolean): String { - return if (sendAction) { + open fun toWorkBlockedByPolicyMessage(share: Boolean): String { + return if (share) { resources.getString(R.string.resolver_cant_share_with_work_apps_explanation) } else { resources.getString(R.string.resolver_cant_access_work_apps_explanation) } } + open fun toPrivateBlockedByPolicyMessage(share: Boolean): String { + return if (share) { + resources.getString(R.string.resolver_cant_share_with_private_apps_explanation) + } else { + resources.getString(R.string.resolver_cant_access_private_apps_explanation) + } + } + fun getWorkProfileNotSupportedMessage(launcherName: String): String { return requireNotNull( policyResources.getString( diff --git a/java/src/com/android/intentresolver/emptystate/CompositeEmptyStateProvider.java b/java/src/com/android/intentresolver/emptystate/CompositeEmptyStateProvider.kt index 41422b66..05062a4b 100644 --- a/java/src/com/android/intentresolver/emptystate/CompositeEmptyStateProvider.java +++ b/java/src/com/android/intentresolver/emptystate/CompositeEmptyStateProvider.kt @@ -13,34 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.intentresolver.emptystate; +package com.android.intentresolver.emptystate -import android.annotation.Nullable; - -import com.android.intentresolver.ResolverListAdapter; +import com.android.intentresolver.ResolverListAdapter /** * Empty state provider that combines multiple providers. Providers earlier in the list have * priority, that is if there is a provider that returns non-null empty state then all further * providers will be ignored. */ -public class CompositeEmptyStateProvider implements EmptyStateProvider { - - private final EmptyStateProvider[] mProviders; - - public CompositeEmptyStateProvider(EmptyStateProvider... providers) { - mProviders = providers; - } +class CompositeEmptyStateProvider( + private vararg val providers: EmptyStateProvider, +) : EmptyStateProvider { - @Nullable - @Override - public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) { - for (EmptyStateProvider provider : mProviders) { - EmptyState emptyState = provider.getEmptyState(resolverListAdapter); - if (emptyState != null) { - return emptyState; - } - } - return null; + override fun getEmptyState(resolverListAdapter: ResolverListAdapter): EmptyState? { + return providers.firstNotNullOfOrNull { it.getEmptyState(resolverListAdapter) } } } diff --git a/java/src/com/android/intentresolver/emptystate/DefaultEmptyState.kt b/java/src/com/android/intentresolver/emptystate/DefaultEmptyState.kt new file mode 100644 index 00000000..ea1a03cc --- /dev/null +++ b/java/src/com/android/intentresolver/emptystate/DefaultEmptyState.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.intentresolver.emptystate + +class DefaultEmptyState : EmptyState { + override fun useDefaultEmptyView() = true +} diff --git a/java/src/com/android/intentresolver/emptystate/DevicePolicyBlockerEmptyState.java b/java/src/com/android/intentresolver/emptystate/DevicePolicyBlockerEmptyState.java index b627636e..1cbc6175 100644 --- a/java/src/com/android/intentresolver/emptystate/DevicePolicyBlockerEmptyState.java +++ b/java/src/com/android/intentresolver/emptystate/DevicePolicyBlockerEmptyState.java @@ -17,40 +17,26 @@ package com.android.intentresolver.emptystate; import android.app.admin.DevicePolicyEventLogger; -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; /** * Empty state that gets strings from the device policy manager and tracks events into * event logger of the device policy events. */ public class DevicePolicyBlockerEmptyState implements EmptyState { - - @NonNull - private final Context mContext; - private final String mDevicePolicyStringTitleId; - @StringRes - private final int mDefaultTitleResource; - private final String mDevicePolicyStringSubtitleId; - @StringRes - private final int mDefaultSubtitleResource; + private final String mTitle; + private final String mSubtitle; private final int mEventId; - @NonNull private final String mEventCategory; - public DevicePolicyBlockerEmptyState(@NonNull Context context, - String devicePolicyStringTitleId, @StringRes int defaultTitleResource, - String devicePolicyStringSubtitleId, @StringRes int defaultSubtitleResource, - int devicePolicyEventId, @NonNull String devicePolicyEventCategory) { - mContext = context; - mDevicePolicyStringTitleId = devicePolicyStringTitleId; - mDefaultTitleResource = defaultTitleResource; - mDevicePolicyStringSubtitleId = devicePolicyStringSubtitleId; - mDefaultSubtitleResource = defaultSubtitleResource; + public DevicePolicyBlockerEmptyState( + String title, + String subtitle, + int devicePolicyEventId, + String devicePolicyEventCategory) { + mTitle = title; + mSubtitle = subtitle; mEventId = devicePolicyEventId; mEventCategory = devicePolicyEventCategory; } @@ -58,24 +44,22 @@ public class DevicePolicyBlockerEmptyState implements EmptyState { @Nullable @Override public String getTitle() { - return mContext.getSystemService(DevicePolicyManager.class).getResources().getString( - mDevicePolicyStringTitleId, - () -> mContext.getString(mDefaultTitleResource)); + return mTitle; } @Nullable @Override public String getSubtitle() { - return mContext.getSystemService(DevicePolicyManager.class).getResources().getString( - mDevicePolicyStringSubtitleId, - () -> mContext.getString(mDefaultSubtitleResource)); + return mSubtitle; } @Override public void onEmptyStateShown() { - DevicePolicyEventLogger.createEvent(mEventId) - .setStrings(mEventCategory) - .write(); + if (mEventId != -1) { + DevicePolicyEventLogger.createEvent(mEventId) + .setStrings(mEventCategory) + .write(); + } } @Override diff --git a/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java b/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java index cd1448e4..b3d3e343 100644 --- a/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java +++ b/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java @@ -70,13 +70,4 @@ public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider { ); } } - - - public static class DefaultEmptyState implements EmptyState { - @Override - public boolean useDefaultEmptyView() { - return true; - } - } - } diff --git a/java/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProvider.java b/java/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProvider.java index fa33928b..0cf2ea45 100644 --- a/java/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProvider.java +++ b/java/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProvider.java @@ -16,15 +16,21 @@ package com.android.intentresolver.emptystate; +import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; +import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; + +import static com.android.intentresolver.ChooserActivity.METRICS_CATEGORY_CHOOSER; + +import static java.util.Objects.requireNonNull; + import android.content.Intent; -import android.os.UserHandle; import androidx.annotation.Nullable; import com.android.intentresolver.ProfileHelper; import com.android.intentresolver.ResolverListAdapter; +import com.android.intentresolver.data.repository.DevicePolicyResources; import com.android.intentresolver.shared.model.Profile; -import com.android.intentresolver.shared.model.User; import java.util.List; @@ -35,55 +41,78 @@ import java.util.List; public class NoCrossProfileEmptyStateProvider implements EmptyStateProvider { private final ProfileHelper mProfileHelper; - private final EmptyState mNoWorkToPersonalEmptyState; - private final EmptyState mNoPersonalToWorkEmptyState; + private final DevicePolicyResources mDevicePolicyResources; + private final boolean mIsShare; private final CrossProfileIntentsChecker mCrossProfileIntentsChecker; public NoCrossProfileEmptyStateProvider( ProfileHelper profileHelper, - EmptyState noWorkToPersonalEmptyState, - EmptyState noPersonalToWorkEmptyState, - CrossProfileIntentsChecker crossProfileIntentsChecker) { + DevicePolicyResources devicePolicyResources, + CrossProfileIntentsChecker crossProfileIntentsChecker, + boolean isShare) { mProfileHelper = profileHelper; - mNoWorkToPersonalEmptyState = noWorkToPersonalEmptyState; - mNoPersonalToWorkEmptyState = noPersonalToWorkEmptyState; + mDevicePolicyResources = devicePolicyResources; + mIsShare = isShare; mCrossProfileIntentsChecker = crossProfileIntentsChecker; } - private boolean anyCrossProfileAllowedIntents(ResolverListAdapter selected, UserHandle source) { - List<Intent> intents = selected.getIntents(); - UserHandle target = selected.getUserHandle(); + private boolean hasCrossProfileIntents(List<Intent> intents, Profile source, Profile target) { + if (source.getPrimary().getHandle().equals(target.getPrimary().getHandle())) { + return true; + } + // Note: Use of getPrimary() here also handles delegation of CLONE profile to parent. return mCrossProfileIntentsChecker.hasCrossProfileIntents(intents, - source.getIdentifier(), target.getIdentifier()); + source.getPrimary().getId(), target.getPrimary().getId()); } @Nullable @Override public EmptyState getEmptyState(ResolverListAdapter adapter) { - Profile launchedAsProfile = mProfileHelper.getLaunchedAsProfile(); - User launchedAs = mProfileHelper.getLaunchedAsProfile().getPrimary(); - UserHandle tabOwnerHandle = adapter.getUserHandle(); - boolean launchedAsSameUser = launchedAs.getHandle().equals(tabOwnerHandle); - Profile.Type tabOwnerType = mProfileHelper.findProfileType(tabOwnerHandle); - - // Not applicable for private profile. - if (launchedAsProfile.getType() == Profile.Type.PRIVATE - || tabOwnerType == Profile.Type.PRIVATE) { - return null; + Profile launchedBy = mProfileHelper.getLaunchedAsProfile(); + Profile tabOwner = requireNonNull(mProfileHelper.findProfile(adapter.getUserHandle())); + + // When sharing into or out of Private profile, perform the check using the parent profile + // instead. (Hard-coded application of CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT) + + Profile effectiveSource = launchedBy; + Profile effectiveTarget = tabOwner; + + // Assumption baked into design: "Personal" profile is the parent of all other profiles. + if (launchedBy.getType() == Profile.Type.PRIVATE) { + effectiveSource = mProfileHelper.getPersonalProfile(); + } + + if (tabOwner.getType() == Profile.Type.PRIVATE) { + effectiveTarget = mProfileHelper.getPersonalProfile(); } - // Allow access to the tab when launched by the same user as the tab owner - // or when there is at least one target which is permitted for cross-profile. - if (launchedAsSameUser || anyCrossProfileAllowedIntents(adapter, - /* source = */ launchedAs.getHandle())) { + // Allow access to the tab when there is at least one target permitted to cross profiles. + if (hasCrossProfileIntents(adapter.getIntents(), effectiveSource, effectiveTarget)) { return null; } - switch (launchedAsProfile.getType()) { - case WORK: return mNoWorkToPersonalEmptyState; - case PERSONAL: return mNoPersonalToWorkEmptyState; + switch (tabOwner.getType()) { + case PERSONAL: + return new DevicePolicyBlockerEmptyState( + mDevicePolicyResources.getCrossProfileBlocked(), + mDevicePolicyResources.toPersonalBlockedByPolicyMessage(mIsShare), + RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL, + METRICS_CATEGORY_CHOOSER); + + case WORK: + return new DevicePolicyBlockerEmptyState( + mDevicePolicyResources.getCrossProfileBlocked(), + mDevicePolicyResources.toWorkBlockedByPolicyMessage(mIsShare), + RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK, + METRICS_CATEGORY_CHOOSER); + + case PRIVATE: + return new DevicePolicyBlockerEmptyState( + mDevicePolicyResources.getCrossProfileBlocked(), + mDevicePolicyResources.toPrivateBlockedByPolicyMessage(mIsShare), + /* Suppress log event. TODO: Define a new metrics event for this? */ -1, + METRICS_CATEGORY_CHOOSER); } return null; } - } diff --git a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt new file mode 100644 index 00000000..b3054231 --- /dev/null +++ b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.intentresolver.icons + +import android.content.ComponentName +import android.graphics.drawable.Drawable +import android.os.UserHandle +import androidx.collection.LruCache +import com.android.intentresolver.chooser.DisplayResolveInfo +import com.android.intentresolver.chooser.SelectableTargetInfo +import java.util.function.Consumer +import javax.annotation.concurrent.GuardedBy +import javax.inject.Qualifier + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.BINARY) annotation class Caching + +private typealias IconCache = LruCache<ComponentName, Drawable> + +class CachingTargetDataLoader( + private val targetDataLoader: TargetDataLoader, + private val cacheSize: Int = 100, +) : TargetDataLoader() { + @GuardedBy("self") private val perProfileIconCache = HashMap<UserHandle, IconCache>() + + override fun getOrLoadAppTargetIcon( + info: DisplayResolveInfo, + userHandle: UserHandle, + callback: Consumer<Drawable> + ): Drawable? { + val cacheKey = info.toCacheKey() + return getCachedAppIcon(cacheKey, userHandle) + ?: targetDataLoader.getOrLoadAppTargetIcon(info, userHandle) { drawable -> + getProfileIconCache(userHandle).put(cacheKey, drawable) + callback.accept(drawable) + } + } + + override fun loadDirectShareIcon( + info: SelectableTargetInfo, + userHandle: UserHandle, + callback: Consumer<Drawable> + ) = targetDataLoader.loadDirectShareIcon(info, userHandle, callback) + + override fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) = + targetDataLoader.loadLabel(info, callback) + + override fun getOrLoadLabel(info: DisplayResolveInfo) = targetDataLoader.getOrLoadLabel(info) + + private fun getCachedAppIcon(component: ComponentName, userHandle: UserHandle): Drawable? = + getProfileIconCache(userHandle)[component] + + private fun getProfileIconCache(userHandle: UserHandle): IconCache = + synchronized(perProfileIconCache) { + perProfileIconCache.getOrPut(userHandle) { IconCache(cacheSize) } + } + + private fun DisplayResolveInfo.toCacheKey() = + ComponentName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name, + ) +} diff --git a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt index 054fbe71..1a724d73 100644 --- a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt @@ -62,11 +62,11 @@ class DefaultTargetDataLoader( ) } - override fun loadAppTargetIcon( + override fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, callback: Consumer<Drawable>, - ) { + ): Drawable? { val taskId = nextTaskId.getAndIncrement() LoadIconTask(context, info, userHandle, presentationFactory) { result -> removeTask(taskId) @@ -74,6 +74,7 @@ class DefaultTargetDataLoader( } .also { addTask(taskId, it) } .executeOnExecutor(executor) + return null } override fun loadDirectShareIcon( diff --git a/java/src/com/android/intentresolver/icons/LabelInfo.kt b/java/src/com/android/intentresolver/icons/LabelInfo.kt index a9c4cd77..4b60d607 100644 --- a/java/src/com/android/intentresolver/icons/LabelInfo.kt +++ b/java/src/com/android/intentresolver/icons/LabelInfo.kt @@ -16,4 +16,4 @@ package com.android.intentresolver.icons -class LabelInfo(val label: CharSequence?, val subLabel: CharSequence?) +data class LabelInfo(val label: CharSequence?, val subLabel: CharSequence?) diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt index 07c62177..7789df44 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt @@ -25,11 +25,11 @@ import java.util.function.Consumer /** A target data loader contract. Added to support testing. */ abstract class TargetDataLoader { /** Load an app target icon */ - abstract fun loadAppTargetIcon( + abstract fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, callback: Consumer<Drawable>, - ) + ): Drawable? /** Load a shortcut icon */ abstract fun loadDirectShareIcon( diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt index 32c040b8..9c0acb11 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt @@ -35,4 +35,10 @@ object TargetDataLoaderModule { @ActivityContext context: Context, @ActivityOwned lifecycle: Lifecycle, ): TargetDataLoader = DefaultTargetDataLoader(context, lifecycle, isAudioCaptureDevice = false) + + @Provides + @ActivityScoped + @Caching + fun cachingTargetDataLoader(targetDataLoader: TargetDataLoader): TargetDataLoader = + CachingTargetDataLoader(targetDataLoader) } diff --git a/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java b/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java index 30858c8e..b46d8bc3 100644 --- a/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java +++ b/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java @@ -171,11 +171,12 @@ public class ResolverWrapperActivity extends ResolverActivity { } @Override - public void loadAppTargetIcon( + @Nullable + public Drawable getOrLoadAppTargetIcon( @NonNull DisplayResolveInfo info, @NonNull UserHandle userHandle, @NonNull Consumer<Drawable> callback) { - mTargetDataLoader.loadAppTargetIcon(info, userHandle, callback); + return mTargetDataLoader.getOrLoadAppTargetIcon(info, userHandle, callback); } @Override diff --git a/tests/integration/src/com/android/intentresolver/v2/data/repository/PlaceholderTest.kt b/tests/integration/src/com/android/intentresolver/v2/data/repository/PlaceholderTest.kt index b66a1906..af2836aa 100644 --- a/tests/integration/src/com/android/intentresolver/v2/data/repository/PlaceholderTest.kt +++ b/tests/integration/src/com/android/intentresolver/v2/data/repository/PlaceholderTest.kt @@ -21,7 +21,5 @@ import org.junit.Test class PlaceholderTest { /** Allows this test target to function while tests are being developed. */ - @Test - fun placeHolder() { - } + @Test fun placeHolder() {} } diff --git a/tests/shared/src/com/android/intentresolver/MockitoKotlinHelpers.kt b/tests/shared/src/com/android/intentresolver/MockitoKotlinHelpers.kt index 40ee6325..755262ee 100644 --- a/tests/shared/src/com/android/intentresolver/MockitoKotlinHelpers.kt +++ b/tests/shared/src/com/android/intentresolver/MockitoKotlinHelpers.kt @@ -18,6 +18,7 @@ package com.android.intentresolver +import kotlin.DeprecationLevel.ERROR import kotlin.DeprecationLevel.WARNING import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatcher @@ -45,7 +46,7 @@ import org.mockito.stubbing.Stubber @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "eq", imports = ["org.mockito.kotlin.eq"]), - level = WARNING + level = ERROR ) inline fun <T> eq(obj: T): T = Mockito.eq<T>(obj) ?: obj @@ -58,7 +59,7 @@ inline fun <T> eq(obj: T): T = Mockito.eq<T>(obj) ?: obj @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "same(obj)", imports = ["org.mockito.kotlin.same"]), - level = WARNING + level = ERROR ) inline fun <T> same(obj: T): T = Mockito.same<T>(obj) ?: obj @@ -103,7 +104,7 @@ inline fun <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "anyOrNull()", imports = ["org.mockito.kotlin.anyOrNull"]), - level = WARNING + level = ERROR ) inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java) @@ -118,7 +119,7 @@ inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java) @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "capture(argumentCaptor)", imports = ["org.mockito.kotlin.capture"]), - level = WARNING + level = ERROR ) inline fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() @@ -132,7 +133,7 @@ inline fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.ca @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "argumentCaptor()", imports = ["org.mockito.kotlin.argumentCaptor"]), - level = WARNING + level = ERROR ) inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> = ArgumentCaptor.forClass(T::class.java) @@ -169,7 +170,7 @@ inline fun <reified T : Any> mock( @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "anyArray()", imports = ["org.mockito.kotlin.anyArray"]), - level = WARNING + level = ERROR ) inline fun <reified T : Any?> anyArray(): Array<T> = Mockito.any(Array<T>::class.java) ?: arrayOf() @@ -183,7 +184,7 @@ inline fun <reified T : Any?> anyArray(): Array<T> = Mockito.any(Array<T>::class @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "whenever(methodCall)", imports = ["org.mockito.kotlin.whenever"]), - level = WARNING + level = ERROR ) inline fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall) @@ -202,7 +203,7 @@ inline fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(meth @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "whenever(mock)", imports = ["org.mockito.kotlin.whenever"]), - level = WARNING + level = ERROR ) inline fun <T> Stubber.whenever(mock: T): T = `when`(mock) @@ -280,7 +281,7 @@ inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Un @Deprecated( "Replace with mockito-kotlin. See http://go/mockito-kotlin", ReplaceWith(expression = "anyOrNull()", imports = ["org.mockito.kotlin.anyOrNull"]), - level = WARNING + level = ERROR ) inline fun <reified T> anyOrNull() = ArgumentMatchers.argThat(ArgumentMatcher<T?> { true }) @@ -288,5 +289,5 @@ inline fun <reified T> anyOrNull() = ArgumentMatchers.argThat(ArgumentMatcher<T? * @see org.mockito.kotlin.mock * @see org.mockito.kotlin.doThrow */ -@Deprecated("Replace with mockito-kotlin. See http://go/mockito-kotlin", level = WARNING) +@Deprecated("Replace with mockito-kotlin. See http://go/mockito-kotlin", level = ERROR) val THROWS_EXCEPTION = Answer { error("Unstubbed behavior was accessed.") } diff --git a/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt index 0c2ae800..8dfbdbdd 100644 --- a/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt +++ b/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt @@ -42,6 +42,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.verify diff --git a/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt b/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt index e974cb7d..df0c5e5e 100644 --- a/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt +++ b/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt @@ -31,29 +31,33 @@ import com.android.intentresolver.util.TestExecutor import com.android.internal.logging.InstanceId import com.google.common.truth.Truth.assertThat import org.junit.Test -import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever class ChooserListAdapterDataTest { private val layoutInflater = mock<LayoutInflater>() private val packageManager = mock<PackageManager>() - private val userManager = mock<UserManager> { whenever(isManagedProfile).thenReturn(false) } + private val userManager = mock<UserManager> { on { isManagedProfile } doReturn false } private val resources = mock<android.content.res.Resources> { - whenever(getInteger(R.integer.config_maxShortcutTargetsPerApp)).thenReturn(2) + on { getInteger(R.integer.config_maxShortcutTargetsPerApp) } doReturn 2 } private val context = mock<Context> { - whenever(getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(layoutInflater) - whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager) - whenever(packageManager).thenReturn(this@ChooserListAdapterDataTest.packageManager) - whenever(resources).thenReturn(this@ChooserListAdapterDataTest.resources) + on { getSystemService(Context.LAYOUT_INFLATER_SERVICE) } doReturn layoutInflater + on { getSystemService(Context.USER_SERVICE) } doReturn userManager + on { packageManager } doReturn this@ChooserListAdapterDataTest.packageManager + on { resources } doReturn this@ChooserListAdapterDataTest.resources } private val targetIntent = Intent(Intent.ACTION_SEND) private val payloadIntents = listOf(targetIntent) private val resolverListController = mock<ResolverListController> { - whenever(filterIneligibleActivities(any(), Mockito.anyBoolean())).thenReturn(null) - whenever(filterLowPriority(any(), Mockito.anyBoolean())).thenReturn(null) + on { filterIneligibleActivities(any(), any()) } doReturn null + on { filterLowPriority(any(), any()) } doReturn null } private val resolverListCommunicator = FakeResolverListCommunicator() private val userHandle = UserHandle.of(UserHandle.USER_CURRENT) diff --git a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt index 3c23ff26..5ac4f2b0 100644 --- a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt +++ b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt @@ -39,8 +39,11 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class ChooserListAdapterTest { @@ -49,7 +52,7 @@ class ChooserListAdapterTest { private val packageManager = mock<PackageManager> { - whenever(resolveActivity(any(), any<ResolveInfoFlags>())).thenReturn(mock()) + on { resolveActivity(any(), any<ResolveInfoFlags>()) } doReturn (mock()) } private val context = InstrumentationRegistry.getInstrumentation().context private val resolverListController = mock<ResolverListController>() @@ -137,7 +140,7 @@ class ChooserListAdapterTest { testSubject.onBindView(view, targetInfo, 0) - verify(mTargetDataLoader, times(1)).loadAppTargetIcon(any(), any(), any()) + verify(mTargetDataLoader, times(1)).getOrLoadAppTargetIcon(any(), any(), any()) } @Test diff --git a/tests/unit/src/com/android/intentresolver/ChooserRefinementManagerTest.kt b/tests/unit/src/com/android/intentresolver/ChooserRefinementManagerTest.kt index 16c917b0..f5210f71 100644 --- a/tests/unit/src/com/android/intentresolver/ChooserRefinementManagerTest.kt +++ b/tests/unit/src/com/android/intentresolver/ChooserRefinementManagerTest.kt @@ -37,8 +37,11 @@ import java.util.concurrent.TimeUnit import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) @UiThreadTest @@ -95,22 +98,21 @@ class ChooserRefinementManagerTest { ) .isTrue() - val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) - Mockito.verify(intentSender) - .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) + val intentCaptor = argumentCaptor<Intent>() + verify(intentSender).sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) - val intent = intentCaptor.value - assertThat(intent?.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)) + val intent = intentCaptor.firstValue + assertThat(intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)) .isEqualTo(exampleSourceIntents[0]) val alternates = - intent?.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java) + intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java) assertThat(alternates?.size).isEqualTo(1) assertThat(alternates?.get(0)).isEqualTo(exampleSourceIntents[1]) // Complete the refinement val receiver = - intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java) + intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java) val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) } receiver?.send(Activity.RESULT_OK, bundle) @@ -130,11 +132,10 @@ class ChooserRefinementManagerTest { ) .isTrue() - val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) - Mockito.verify(intentSender) - .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) + val intentCaptor = argumentCaptor<Intent>() + verify(intentSender).sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) - val intent = intentCaptor.value + val intent = intentCaptor.firstValue // Complete the refinement val receiver = diff --git a/tests/unit/src/com/android/intentresolver/TargetPresentationGetterTest.kt b/tests/unit/src/com/android/intentresolver/TargetPresentationGetterTest.kt index e62672a3..92848b2c 100644 --- a/tests/unit/src/com/android/intentresolver/TargetPresentationGetterTest.kt +++ b/tests/unit/src/com/android/intentresolver/TargetPresentationGetterTest.kt @@ -16,189 +16,211 @@ package com.android.intentresolver -import com.android.intentresolver.ResolverDataProvider import com.google.common.truth.Truth.assertThat import org.junit.Test /** * Unit tests for the various implementations of {@link TargetPresentationGetter}. + * * TODO: consider expanding to cover icon logic (not just labels/sublabels). * TODO: these are conceptually "acceptance tests" that provide comprehensive coverage of the - * apparent variations in the legacy implementation. The tests probably don't have to be so - * exhaustive if we're able to impose a simpler design on the implementation. + * apparent variations in the legacy implementation. The tests probably don't have to be so + * exhaustive if we're able to impose a simpler design on the implementation. */ class TargetPresentationGetterTest { - fun makeResolveInfoPresentationGetter( - withSubstitutePermission: Boolean, - appLabel: String, - activityLabel: String, - resolveInfoLabel: String): TargetPresentationGetter { - val testPackageInfo = ResolverDataProvider.createPackageManagerMockedInfo( - withSubstitutePermission, appLabel, activityLabel, resolveInfoLabel) - val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100) - return factory.makePresentationGetter(testPackageInfo.resolveInfo) - } - - fun makeActivityInfoPresentationGetter( - withSubstitutePermission: Boolean, - appLabel: String?, - activityLabel: String?): TargetPresentationGetter { - val testPackageInfo = ResolverDataProvider.createPackageManagerMockedInfo( - withSubstitutePermission, appLabel, activityLabel, "") - val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100) - return factory.makePresentationGetter(testPackageInfo.activityInfo) - } - - @Test - fun testActivityInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter( - false, "app_label", "activity_label") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") - } - - @Test - fun testActivityInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter( - false, "app_label", "app_label") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // Without the substitute permission, there's no logic to dedupe the labels. - // TODO: this matches our observations in the legacy code, but is it the right behavior? It - // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in - // the UI at least, but maybe that logic should be pulled back to the "presentation"? - assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label") - } - - @Test - fun testActivityInfoLabels_noSubstitutePermission_nullRequestedLabel() { - val presentationGetter = makeActivityInfoPresentationGetter(false, null, "activity_label") - assertThat(presentationGetter.getLabel()).isNull() - assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") - } - - @Test - fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedLabel() { - val presentationGetter = makeActivityInfoPresentationGetter(false, "", "activity_label") - assertThat(presentationGetter.getLabel()).isEqualTo("") - assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") - } - - @Test - fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter(false, "app_label", "") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // Without the substitute permission, empty sublabels are passed through as-is. - assertThat(presentationGetter.getSubLabel()).isEqualTo("") - } - - @Test - fun testActivityInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter( - true, "app_label", "activity_label") - assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") - // With the substitute permission, the same ("activity") label is requested as both the label - // and sublabel, even though the other value ("app_label") was distinct. Thus this behaves the - // same as a dupe. - assertThat(presentationGetter.getSubLabel()).isEqualTo(null) - } - - @Test - fun testActivityInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter( - true, "app_label", "app_label") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // With the substitute permission, duped sublabels get converted to nulls. - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testActivityInfoLabels_withSubstitutePermission_nullRequestedLabel() { - val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", null) - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // With the substitute permission, null inputs are a special case that produces null outputs - // (i.e., they're not simply passed-through from the inputs). - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedLabel() { - val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", "") - // Empty "labels" are taken as-is and (unlike nulls) don't prompt a fallback to the sublabel. - // Thus (as in the previous case with substitute permission & "distinct" labels), this is - // treated as a dupe. - assertThat(presentationGetter.getLabel()).isEqualTo("") - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedSublabel() { - val presentationGetter = makeActivityInfoPresentationGetter(true, "", "activity_label") - assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") - // With the substitute permission, empty sublabels get converted to nulls. - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testResolveInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - false, "app_label", "activity_label", "resolve_info_label") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label") - } - - @Test - fun testResolveInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - false, "app_label", "activity_label", "app_label") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // Without the substitute permission, there's no logic to dedupe the labels. - // TODO: this matches our observations in the legacy code, but is it the right behavior? It - // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in - // the UI at least, but maybe that logic should be pulled back to the "presentation"? - assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label") - } - - @Test - fun testResolveInfoLabels_noSubstitutePermission_emptyRequestedSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - false, "app_label", "activity_label", "") - assertThat(presentationGetter.getLabel()).isEqualTo("app_label") - // Without the substitute permission, empty sublabels are passed through as-is. - assertThat(presentationGetter.getSubLabel()).isEqualTo("") - } - - @Test - fun testResolveInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - true, "app_label", "activity_label", "resolve_info_label") - assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") - assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label") - } - - @Test - fun testResolveInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - true, "app_label", "activity_label", "activity_label") - assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") - // With the substitute permission, duped sublabels get converted to nulls. - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - true, "app_label", "activity_label", "") - assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") - // With the substitute permission, empty sublabels get converted to nulls. - assertThat(presentationGetter.getSubLabel()).isNull() - } - - @Test - fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedLabelAndSublabel() { - val presentationGetter = makeResolveInfoPresentationGetter( - true, "app_label", "", "") - assertThat(presentationGetter.getLabel()).isEqualTo("") - // With the substitute permission, empty sublabels get converted to nulls. - assertThat(presentationGetter.getSubLabel()).isNull() - } + fun makeResolveInfoPresentationGetter( + withSubstitutePermission: Boolean, + appLabel: String, + activityLabel: String, + resolveInfoLabel: String + ): TargetPresentationGetter { + val testPackageInfo = + ResolverDataProvider.createPackageManagerMockedInfo( + withSubstitutePermission, + appLabel, + activityLabel, + resolveInfoLabel + ) + val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100) + return factory.makePresentationGetter(testPackageInfo.resolveInfo) + } + + fun makeActivityInfoPresentationGetter( + withSubstitutePermission: Boolean, + appLabel: String?, + activityLabel: String? + ): TargetPresentationGetter { + val testPackageInfo = + ResolverDataProvider.createPackageManagerMockedInfo( + withSubstitutePermission, + appLabel, + activityLabel, + "" + ) + val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100) + return factory.makePresentationGetter(testPackageInfo.activityInfo) + } + + @Test + fun testActivityInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() { + val presentationGetter = + makeActivityInfoPresentationGetter(false, "app_label", "activity_label") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") + } + + @Test + fun testActivityInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() { + val presentationGetter = makeActivityInfoPresentationGetter(false, "app_label", "app_label") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // Without the substitute permission, there's no logic to dedupe the labels. + // TODO: this matches our observations in the legacy code, but is it the right behavior? It + // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in + // the UI at least, but maybe that logic should be pulled back to the "presentation"? + assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label") + } + + @Test + fun testActivityInfoLabels_noSubstitutePermission_nullRequestedLabel() { + val presentationGetter = makeActivityInfoPresentationGetter(false, null, "activity_label") + assertThat(presentationGetter.getLabel()).isNull() + assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") + } + + @Test + fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedLabel() { + val presentationGetter = makeActivityInfoPresentationGetter(false, "", "activity_label") + assertThat(presentationGetter.getLabel()).isEqualTo("") + assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label") + } + + @Test + fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedSublabel() { + val presentationGetter = makeActivityInfoPresentationGetter(false, "app_label", "") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // Without the substitute permission, empty sublabels are passed through as-is. + assertThat(presentationGetter.getSubLabel()).isEqualTo("") + } + + @Test + fun testActivityInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() { + val presentationGetter = + makeActivityInfoPresentationGetter(true, "app_label", "activity_label") + assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") + // With the substitute permission, the same ("activity") label is requested as both the + // label + // and sublabel, even though the other value ("app_label") was distinct. Thus this behaves + // the + // same as a dupe. + assertThat(presentationGetter.getSubLabel()).isEqualTo(null) + } + + @Test + fun testActivityInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() { + val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", "app_label") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // With the substitute permission, duped sublabels get converted to nulls. + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testActivityInfoLabels_withSubstitutePermission_nullRequestedLabel() { + val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", null) + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // With the substitute permission, null inputs are a special case that produces null outputs + // (i.e., they're not simply passed-through from the inputs). + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedLabel() { + val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", "") + // Empty "labels" are taken as-is and (unlike nulls) don't prompt a fallback to the + // sublabel. + // Thus (as in the previous case with substitute permission & "distinct" labels), this is + // treated as a dupe. + assertThat(presentationGetter.getLabel()).isEqualTo("") + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedSublabel() { + val presentationGetter = makeActivityInfoPresentationGetter(true, "", "activity_label") + assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") + // With the substitute permission, empty sublabels get converted to nulls. + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testResolveInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter( + false, + "app_label", + "activity_label", + "resolve_info_label" + ) + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label") + } + + @Test + fun testResolveInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter(false, "app_label", "activity_label", "app_label") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // Without the substitute permission, there's no logic to dedupe the labels. + // TODO: this matches our observations in the legacy code, but is it the right behavior? It + // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in + // the UI at least, but maybe that logic should be pulled back to the "presentation"? + assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label") + } + + @Test + fun testResolveInfoLabels_noSubstitutePermission_emptyRequestedSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter(false, "app_label", "activity_label", "") + assertThat(presentationGetter.getLabel()).isEqualTo("app_label") + // Without the substitute permission, empty sublabels are passed through as-is. + assertThat(presentationGetter.getSubLabel()).isEqualTo("") + } + + @Test + fun testResolveInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter( + true, + "app_label", + "activity_label", + "resolve_info_label" + ) + assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") + assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label") + } + + @Test + fun testResolveInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter(true, "app_label", "activity_label", "activity_label") + assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") + // With the substitute permission, duped sublabels get converted to nulls. + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedSublabel() { + val presentationGetter = + makeResolveInfoPresentationGetter(true, "app_label", "activity_label", "") + assertThat(presentationGetter.getLabel()).isEqualTo("activity_label") + // With the substitute permission, empty sublabels get converted to nulls. + assertThat(presentationGetter.getSubLabel()).isNull() + } + + @Test + fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedLabelAndSublabel() { + val presentationGetter = makeResolveInfoPresentationGetter(true, "app_label", "", "") + assertThat(presentationGetter.getLabel()).isEqualTo("") + // With the substitute permission, empty sublabels get converted to nulls. + assertThat(presentationGetter.getSubLabel()).isNull() + } } diff --git a/tests/unit/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt b/tests/unit/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt index 6712bf31..4d9d4880 100644 --- a/tests/unit/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt +++ b/tests/unit/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt @@ -23,17 +23,17 @@ import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.os.UserHandle -import com.android.intentresolver.createShortcutInfo -import com.android.intentresolver.mock +import androidx.test.platform.app.InstrumentationRegistry import com.android.intentresolver.ResolverActivity import com.android.intentresolver.ResolverDataProvider +import com.android.intentresolver.createShortcutInfo import com.google.common.truth.Truth.assertThat import org.junit.Test -import androidx.test.platform.app.InstrumentationRegistry +import org.mockito.kotlin.mock class ImmutableTargetInfoTest { - private val PERSONAL_USER_HANDLE: UserHandle = InstrumentationRegistry - .getInstrumentation().getTargetContext().getUser() + private val PERSONAL_USER_HANDLE: UserHandle = + InstrumentationRegistry.getInstrumentation().getTargetContext().getUser() private val resolvedIntent = Intent("resolved") private val targetIntent = Intent("target") @@ -46,61 +46,62 @@ class ImmutableTargetInfoTest { private val displayIconHolder: TargetInfo.IconHolder = mock() private val sourceIntent1 = Intent("source1") private val sourceIntent2 = Intent("source2") - private val displayTarget1 = DisplayResolveInfo.newDisplayResolveInfo( - Intent("display1"), - ResolverDataProvider.createResolveInfo(2, 0, PERSONAL_USER_HANDLE), - "display1 label", - "display1 extended info", - Intent("display1_resolved") - ) - private val displayTarget2 = DisplayResolveInfo.newDisplayResolveInfo( - Intent("display2"), - ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE), - "display2 label", - "display2 extended info", - Intent("display2_resolved") - ) - private val directShareShortcutInfo = createShortcutInfo( - "shortcutid", ResolverDataProvider.createComponentName(4), 4) - private val directShareAppTarget = AppTarget( - AppTargetId("apptargetid"), - "test.directshare", - "target", - UserHandle.CURRENT) - private val displayResolveInfo = DisplayResolveInfo.newDisplayResolveInfo( - Intent("displayresolve"), - ResolverDataProvider.createResolveInfo(5, 0, PERSONAL_USER_HANDLE), - "displayresolve label", - "displayresolve extended info", - Intent("display_resolved") - ) + private val displayTarget1 = + DisplayResolveInfo.newDisplayResolveInfo( + Intent("display1"), + ResolverDataProvider.createResolveInfo(2, 0, PERSONAL_USER_HANDLE), + "display1 label", + "display1 extended info", + Intent("display1_resolved") + ) + private val displayTarget2 = + DisplayResolveInfo.newDisplayResolveInfo( + Intent("display2"), + ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE), + "display2 label", + "display2 extended info", + Intent("display2_resolved") + ) + private val directShareShortcutInfo = + createShortcutInfo("shortcutid", ResolverDataProvider.createComponentName(4), 4) + private val directShareAppTarget = + AppTarget(AppTargetId("apptargetid"), "test.directshare", "target", UserHandle.CURRENT) + private val displayResolveInfo = + DisplayResolveInfo.newDisplayResolveInfo( + Intent("displayresolve"), + ResolverDataProvider.createResolveInfo(5, 0, PERSONAL_USER_HANDLE), + "displayresolve label", + "displayresolve extended info", + Intent("display_resolved") + ) private val hashProvider: ImmutableTargetInfo.TargetHashProvider = mock() @Test - fun testBasicProperties() { // Fields that are reflected back w/o logic. + fun testBasicProperties() { // Fields that are reflected back w/o logic. // TODO: we could consider passing copies of all the values into the builder so that we can // verify that they're not mutated (e.g. no extras added to the intents). For now that // should be obvious from the implementation. - val info = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(resolvedIntent) - .setTargetIntent(targetIntent) - .setReferrerFillInIntent(referrerFillInIntent) - .setResolvedComponentName(resolvedComponentName) - .setChooserTargetComponentName(chooserTargetComponentName) - .setResolveInfo(resolveInfo) - .setDisplayLabel(displayLabel) - .setExtendedInfo(extendedInfo) - .setDisplayIconHolder(displayIconHolder) - .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2)) - .setAllDisplayTargets(listOf(displayTarget1, displayTarget2)) - .setIsSuspended(true) - .setIsPinned(true) - .setModifiedScore(42.0f) - .setDirectShareShortcutInfo(directShareShortcutInfo) - .setDirectShareAppTarget(directShareAppTarget) - .setDisplayResolveInfo(displayResolveInfo) - .setHashProvider(hashProvider) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(resolvedIntent) + .setTargetIntent(targetIntent) + .setReferrerFillInIntent(referrerFillInIntent) + .setResolvedComponentName(resolvedComponentName) + .setChooserTargetComponentName(chooserTargetComponentName) + .setResolveInfo(resolveInfo) + .setDisplayLabel(displayLabel) + .setExtendedInfo(extendedInfo) + .setDisplayIconHolder(displayIconHolder) + .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2)) + .setAllDisplayTargets(listOf(displayTarget1, displayTarget2)) + .setIsSuspended(true) + .setIsPinned(true) + .setModifiedScore(42.0f) + .setDirectShareShortcutInfo(directShareShortcutInfo) + .setDirectShareAppTarget(directShareAppTarget) + .setDisplayResolveInfo(displayResolveInfo) + .setHashProvider(hashProvider) + .build() assertThat(info.resolvedIntent).isEqualTo(resolvedIntent) assertThat(info.targetIntent).isEqualTo(targetIntent) @@ -111,8 +112,8 @@ class ImmutableTargetInfoTest { assertThat(info.displayLabel).isEqualTo(displayLabel) assertThat(info.extendedInfo).isEqualTo(extendedInfo) assertThat(info.displayIconHolder).isEqualTo(displayIconHolder) - assertThat(info.allSourceIntents).containsExactly( - resolvedIntent, sourceIntent1, sourceIntent2) + assertThat(info.allSourceIntents) + .containsExactly(resolvedIntent, sourceIntent1, sourceIntent2) assertThat(info.allDisplayTargets).containsExactly(displayTarget1, displayTarget2) assertThat(info.isSuspended).isTrue() assertThat(info.isPinned).isTrue() @@ -134,26 +135,27 @@ class ImmutableTargetInfoTest { fun testToBuilderPreservesBasicProperties() { // Note this is set up exactly as in `testBasicProperties`, but the assertions will be made // against a *copy* of the object instead. - val infoToCopyFrom = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(resolvedIntent) - .setTargetIntent(targetIntent) - .setReferrerFillInIntent(referrerFillInIntent) - .setResolvedComponentName(resolvedComponentName) - .setChooserTargetComponentName(chooserTargetComponentName) - .setResolveInfo(resolveInfo) - .setDisplayLabel(displayLabel) - .setExtendedInfo(extendedInfo) - .setDisplayIconHolder(displayIconHolder) - .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2)) - .setAllDisplayTargets(listOf(displayTarget1, displayTarget2)) - .setIsSuspended(true) - .setIsPinned(true) - .setModifiedScore(42.0f) - .setDirectShareShortcutInfo(directShareShortcutInfo) - .setDirectShareAppTarget(directShareAppTarget) - .setDisplayResolveInfo(displayResolveInfo) - .setHashProvider(hashProvider) - .build() + val infoToCopyFrom = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(resolvedIntent) + .setTargetIntent(targetIntent) + .setReferrerFillInIntent(referrerFillInIntent) + .setResolvedComponentName(resolvedComponentName) + .setChooserTargetComponentName(chooserTargetComponentName) + .setResolveInfo(resolveInfo) + .setDisplayLabel(displayLabel) + .setExtendedInfo(extendedInfo) + .setDisplayIconHolder(displayIconHolder) + .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2)) + .setAllDisplayTargets(listOf(displayTarget1, displayTarget2)) + .setIsSuspended(true) + .setIsPinned(true) + .setModifiedScore(42.0f) + .setDirectShareShortcutInfo(directShareShortcutInfo) + .setDirectShareAppTarget(directShareAppTarget) + .setDisplayResolveInfo(displayResolveInfo) + .setHashProvider(hashProvider) + .build() val info = infoToCopyFrom.toBuilder().build() @@ -166,8 +168,8 @@ class ImmutableTargetInfoTest { assertThat(info.displayLabel).isEqualTo(displayLabel) assertThat(info.extendedInfo).isEqualTo(extendedInfo) assertThat(info.displayIconHolder).isEqualTo(displayIconHolder) - assertThat(info.allSourceIntents).containsExactly( - resolvedIntent, sourceIntent1, sourceIntent2) + assertThat(info.allSourceIntents) + .containsExactly(resolvedIntent, sourceIntent1, sourceIntent2) assertThat(info.allDisplayTargets).containsExactly(displayTarget1, displayTarget2) assertThat(info.isSuspended).isTrue() assertThat(info.isPinned).isTrue() @@ -199,12 +201,13 @@ class ImmutableTargetInfoTest { val referrerFillInIntent = Intent("REFERRER_FILL_IN") referrerFillInIntent.setPackage("referrer") - val info = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .setReferrerFillInIntent(referrerFillInIntent) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(originalIntent) + .setReferrerFillInIntent(referrerFillInIntent) + .build() - assertThat(info.baseIntentToSend.getPackage()).isEqualTo("original") // Only fill if empty. + assertThat(info.baseIntentToSend.getPackage()).isEqualTo("original") // Only fill if empty. assertThat(info.baseIntentToSend.action).isEqualTo("REFERRER_FILL_IN") } @@ -216,13 +219,12 @@ class ImmutableTargetInfoTest { val refinementIntent = Intent() refinementIntent.putExtra("REFINEMENT", true) - val originalInfo = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .build() + val originalInfo = + ImmutableTargetInfo.newBuilder().setResolvedIntent(originalIntent).build() val info = checkNotNull(originalInfo.tryToCloneWithAppliedRefinement(refinementIntent)) - assertThat(info?.baseIntentToSend?.getBooleanExtra("ORIGINAL", false)).isTrue() - assertThat(info?.baseIntentToSend?.getBooleanExtra("REFINEMENT", false)).isTrue() + assertThat(info.baseIntentToSend?.getBooleanExtra("ORIGINAL", false)).isTrue() + assertThat(info.baseIntentToSend?.getBooleanExtra("REFINEMENT", false)).isTrue() } @Test @@ -234,20 +236,21 @@ class ImmutableTargetInfoTest { referrerFillInIntent.setPackage("referrer_pkg") referrerFillInIntent.setType("test/referrer") - val infoWithReferrerFillIn = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .setReferrerFillInIntent(referrerFillInIntent) - .build() + val infoWithReferrerFillIn = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(originalIntent) + .setReferrerFillInIntent(referrerFillInIntent) + .build() val refinementIntent = Intent("REFINE_ME") - refinementIntent.setPackage("original") // Has to match for refinement. + refinementIntent.setPackage("original") // Has to match for refinement. val info = checkNotNull(infoWithReferrerFillIn.tryToCloneWithAppliedRefinement(refinementIntent)) - assertThat(info?.baseIntentToSend?.getPackage()).isEqualTo("original") // Set all along. - assertThat(info?.baseIntentToSend?.action).isEqualTo("REFINE_ME") // Refinement wins. - assertThat(info?.baseIntentToSend?.type).isEqualTo("test/referrer") // Left for referrer. + assertThat(info.baseIntentToSend?.getPackage()).isEqualTo("original") // Set all along. + assertThat(info.baseIntentToSend?.action).isEqualTo("REFINE_ME") // Refinement wins. + assertThat(info.baseIntentToSend?.type).isEqualTo("test/referrer") // Left for referrer. } @Test @@ -260,25 +263,26 @@ class ImmutableTargetInfoTest { val refinementIntent2 = Intent("REFINE_ME") refinementIntent2.putExtra("TEST2", "2") - val originalInfo = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .setReferrerFillInIntent(referrerFillInIntent) - .build() + val originalInfo = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(originalIntent) + .setReferrerFillInIntent(referrerFillInIntent) + .build() val refined1 = checkNotNull(originalInfo.tryToCloneWithAppliedRefinement(refinementIntent1)) // Cloned clone. val refined2 = checkNotNull(refined1.tryToCloneWithAppliedRefinement(refinementIntent2)) // Both clones get the same values filled in from the referrer intent. - assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER") - assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER") + assertThat(refined1.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER") + assertThat(refined2.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER") // Each clone has the respective value that was set in their own refinement request. - assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST1")).isEqualTo("1") - assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST2")).isEqualTo("2") + assertThat(refined1.baseIntentToSend?.getStringExtra("TEST1")).isEqualTo("1") + assertThat(refined2.baseIntentToSend?.getStringExtra("TEST2")).isEqualTo("2") // The clones don't have the data from each other's refinements, even though the intent // field is empty (thus able to be populated by filling-in). - assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST2")).isNull() - assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST1")).isNull() + assertThat(refined1.baseIntentToSend?.getStringExtra("TEST2")).isNull() + assertThat(refined2.baseIntentToSend?.getStringExtra("TEST1")).isNull() } @Test @@ -292,25 +296,27 @@ class ImmutableTargetInfoTest { val extraMatch = Intent("REFINE_ME") extraMatch.putExtra("extraMatch", true) - val originalInfo = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .setAllSourceIntents(listOf( - originalIntent, mismatchedAlternate, targetAlternate, extraMatch)) - .build() + val originalInfo = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(originalIntent) + .setAllSourceIntents( + listOf(originalIntent, mismatchedAlternate, targetAlternate, extraMatch) + ) + .build() - val refinement = Intent("REFINE_ME") // First match is `targetAlternate` + val refinement = Intent("REFINE_ME") // First match is `targetAlternate` refinement.putExtra("refinement", true) val refinedResult = checkNotNull(originalInfo.tryToCloneWithAppliedRefinement(refinement)) - assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("refinement", false)).isTrue() - assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("targetAlternate", false)) + assertThat(refinedResult.baseIntentToSend?.getBooleanExtra("refinement", false)).isTrue() + assertThat(refinedResult.baseIntentToSend?.getBooleanExtra("targetAlternate", false)) .isTrue() // None of the other source intents got merged in (not even the later one that matched): - assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("originalIntent", false)) + assertThat(refinedResult.baseIntentToSend?.getBooleanExtra("originalIntent", false)) .isFalse() - assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("mismatchedAlternate", false)) + assertThat(refinedResult.baseIntentToSend?.getBooleanExtra("mismatchedAlternate", false)) .isFalse() - assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("extraMatch", false)).isFalse() + assertThat(refinedResult.baseIntentToSend?.getBooleanExtra("extraMatch", false)).isFalse() } @Test @@ -320,10 +326,11 @@ class ImmutableTargetInfoTest { val mismatchedAlternate = Intent("DOESNT_MATCH") mismatchedAlternate.putExtra("mismatchedAlternate", true) - val originalInfo = ImmutableTargetInfo.newBuilder() - .setResolvedIntent(originalIntent) - .setAllSourceIntents(listOf(originalIntent, mismatchedAlternate)) - .build() + val originalInfo = + ImmutableTargetInfo.newBuilder() + .setResolvedIntent(originalIntent) + .setAllSourceIntents(listOf(originalIntent, mismatchedAlternate)) + .build() val refinement = Intent("PROPOSED_REFINEMENT") assertThat(originalInfo.tryToCloneWithAppliedRefinement(refinement)).isNull() @@ -331,9 +338,10 @@ class ImmutableTargetInfoTest { @Test fun testLegacySubclassRelationships_empty() { - val info = ImmutableTargetInfo.newBuilder() - .setLegacyType(ImmutableTargetInfo.LegacyTargetType.EMPTY_TARGET_INFO) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setLegacyType(ImmutableTargetInfo.LegacyTargetType.EMPTY_TARGET_INFO) + .build() assertThat(info.isEmptyTargetInfo).isTrue() assertThat(info.isPlaceHolderTargetInfo).isFalse() @@ -346,9 +354,10 @@ class ImmutableTargetInfoTest { @Test fun testLegacySubclassRelationships_placeholder() { - val info = ImmutableTargetInfo.newBuilder() - .setLegacyType(ImmutableTargetInfo.LegacyTargetType.PLACEHOLDER_TARGET_INFO) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setLegacyType(ImmutableTargetInfo.LegacyTargetType.PLACEHOLDER_TARGET_INFO) + .build() assertThat(info.isEmptyTargetInfo).isFalse() assertThat(info.isPlaceHolderTargetInfo).isTrue() @@ -361,9 +370,10 @@ class ImmutableTargetInfoTest { @Test fun testLegacySubclassRelationships_selectable() { - val info = ImmutableTargetInfo.newBuilder() - .setLegacyType(ImmutableTargetInfo.LegacyTargetType.SELECTABLE_TARGET_INFO) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setLegacyType(ImmutableTargetInfo.LegacyTargetType.SELECTABLE_TARGET_INFO) + .build() assertThat(info.isEmptyTargetInfo).isFalse() assertThat(info.isPlaceHolderTargetInfo).isFalse() @@ -376,9 +386,10 @@ class ImmutableTargetInfoTest { @Test fun testLegacySubclassRelationships_displayResolveInfo() { - val info = ImmutableTargetInfo.newBuilder() - .setLegacyType(ImmutableTargetInfo.LegacyTargetType.DISPLAY_RESOLVE_INFO) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setLegacyType(ImmutableTargetInfo.LegacyTargetType.DISPLAY_RESOLVE_INFO) + .build() assertThat(info.isEmptyTargetInfo).isFalse() assertThat(info.isPlaceHolderTargetInfo).isFalse() @@ -391,9 +402,10 @@ class ImmutableTargetInfoTest { @Test fun testLegacySubclassRelationships_multiDisplayResolveInfo() { - val info = ImmutableTargetInfo.newBuilder() - .setLegacyType(ImmutableTargetInfo.LegacyTargetType.MULTI_DISPLAY_RESOLVE_INFO) - .build() + val info = + ImmutableTargetInfo.newBuilder() + .setLegacyType(ImmutableTargetInfo.LegacyTargetType.MULTI_DISPLAY_RESOLVE_INFO) + .build() assertThat(info.isEmptyTargetInfo).isFalse() assertThat(info.isPlaceHolderTargetInfo).isFalse() @@ -406,13 +418,17 @@ class ImmutableTargetInfoTest { @Test fun testActivityStarter_correctNumberOfInvocations_startAsCaller() { - val activityStarter = object : TestActivityStarter() { - override fun startAsUser( - target: TargetInfo, activity: Activity, options: Bundle, user: UserHandle - ): Boolean { - throw RuntimeException("Wrong API used: startAsUser") + val activityStarter = + object : TestActivityStarter() { + override fun startAsUser( + target: TargetInfo, + activity: Activity, + options: Bundle, + user: UserHandle + ): Boolean { + throw RuntimeException("Wrong API used: startAsUser") + } } - } val info = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build() val activity: ResolverActivity = mock() @@ -431,12 +447,17 @@ class ImmutableTargetInfoTest { @Test fun testActivityStarter_correctNumberOfInvocations_startAsUser() { - val activityStarter = object : TestActivityStarter() { - override fun startAsCaller( - target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean { - throw RuntimeException("Wrong API used: startAsCaller") + val activityStarter = + object : TestActivityStarter() { + override fun startAsCaller( + target: TargetInfo, + activity: Activity, + options: Bundle, + userId: Int + ): Boolean { + throw RuntimeException("Wrong API used: startAsCaller") + } } - } val info = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build() val activity: Activity = mock() @@ -466,7 +487,7 @@ class ImmutableTargetInfoTest { info2.startAsUser(mock(), Bundle(), UserHandle.of(42)) assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info2) - assertThat(activityStarter.totalInvocations).isEqualTo(3) // Instance is still shared. + assertThat(activityStarter.totalInvocations).isEqualTo(3) // Instance is still shared. } } @@ -475,27 +496,35 @@ private open class TestActivityStarter : ImmutableTargetInfo.TargetActivityStart var lastInvocationTargetInfo: TargetInfo? = null var lastInvocationActivity: Activity? = null var lastInvocationOptions: Bundle? = null - var lastInvocationUserId: Integer? = null + var lastInvocationUserId: Int? = null var lastInvocationAsCaller = false override fun startAsCaller( - target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean { + target: TargetInfo, + activity: Activity, + options: Bundle, + userId: Int + ): Boolean { ++totalInvocations lastInvocationTargetInfo = target lastInvocationActivity = activity lastInvocationOptions = options - lastInvocationUserId = Integer(userId) + lastInvocationUserId = userId lastInvocationAsCaller = true return true } override fun startAsUser( - target: TargetInfo, activity: Activity, options: Bundle, user: UserHandle): Boolean { + target: TargetInfo, + activity: Activity, + options: Bundle, + user: UserHandle + ): Boolean { ++totalInvocations lastInvocationTargetInfo = target lastInvocationActivity = activity lastInvocationOptions = options - lastInvocationUserId = Integer(user.identifier) + lastInvocationUserId = user.identifier lastInvocationAsCaller = false return true } diff --git a/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt b/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt index b346bee5..a2f6e7a4 100644 --- a/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt +++ b/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt @@ -30,16 +30,16 @@ import com.android.intentresolver.ResolverDataProvider import com.android.intentresolver.ResolverDataProvider.createResolveInfo import com.android.intentresolver.createChooserTarget import com.android.intentresolver.createShortcutInfo -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test -import org.mockito.Mockito.any -import org.mockito.Mockito.never -import org.mockito.Mockito.spy -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify class TargetInfoTest { private val PERSONAL_USER_HANDLE: UserHandle = @@ -422,9 +422,7 @@ class TargetInfoTest { ) ) val targetTwo = - mock<DisplayResolveInfo> { - whenever(tryToCloneWithAppliedRefinement(any())).thenReturn(this) - } + mock<DisplayResolveInfo> { on { tryToCloneWithAppliedRefinement(any()) } doReturn mock } val multiTargetInfo = MultiDisplayResolveInfo.newMultiDisplayResolveInfo(listOf(targetOne, targetTwo)) diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt index e4489bd1..27d98ece 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt @@ -23,8 +23,6 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider import com.android.intentresolver.ContentTypeHint import com.android.intentresolver.FakeImageLoader import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.android.intentresolver.widget.ActionRow import com.android.intentresolver.widget.ImagePreviewView import com.google.common.truth.Truth.assertThat @@ -38,6 +36,8 @@ import org.junit.Test import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever class ChooserContentPreviewUiTest { private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher()) diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt index 0e4e36ab..7c50fa42 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt @@ -23,13 +23,13 @@ import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.intentresolver.R -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.android.intentresolver.widget.ActionRow import com.google.common.truth.Truth.assertThat import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class FileContentPreviewUiTest { @@ -45,7 +45,7 @@ class FileContentPreviewUiTest { override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {} } private val headlineGenerator = - mock<HeadlineGenerator> { whenever(getFilesHeadline(fileCount)).thenReturn(text) } + mock<HeadlineGenerator> { on { getFilesHeadline(fileCount) } doReturn text } private val context get() = InstrumentationRegistry.getInstrumentation().context diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt index da0ddd12..1d85c61b 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt @@ -25,8 +25,6 @@ import androidx.annotation.IdRes import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.intentresolver.R -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.android.intentresolver.widget.ActionRow import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -36,10 +34,12 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify private const val HEADLINE_IMAGES = "Image Headline" private const val HEADLINE_VIDEOS = "Video Headline" @@ -64,9 +64,9 @@ class FilesPlusTextContentPreviewUiTest { private val imageLoader = mock<ImageLoader>() private val headlineGenerator = mock<HeadlineGenerator> { - whenever(getImagesHeadline(anyInt())).thenReturn(HEADLINE_IMAGES) - whenever(getVideosHeadline(anyInt())).thenReturn(HEADLINE_VIDEOS) - whenever(getFilesHeadline(anyInt())).thenReturn(HEADLINE_FILES) + on { getImagesHeadline(any()) } doReturn HEADLINE_IMAGES + on { getVideosHeadline(any()) } doReturn HEADLINE_VIDEOS + on { getFilesHeadline(any()) } doReturn HEADLINE_FILES } private val testMetadataText: CharSequence = "Test metadata text" diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt index 41989bda..3a45e2f6 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt @@ -20,10 +20,6 @@ import android.content.ContentResolver import android.graphics.Bitmap import android.net.Uri import android.util.Size -import com.android.intentresolver.any -import com.android.intentresolver.anyOrNull -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Truth.assertThat import java.util.ArrayDeque import java.util.concurrent.CountDownLatch @@ -52,9 +48,16 @@ import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield import org.junit.Assert.assertTrue import org.junit.Test -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) class ImagePreviewImageLoaderTest { @@ -63,9 +66,7 @@ class ImagePreviewImageLoaderTest { private val uriTwo = Uri.parse("content://org.package.app/image-2.png") private val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) private val contentResolver = - mock<ContentResolver> { - whenever(loadThumbnail(any(), any(), anyOrNull())).thenReturn(bitmap) - } + mock<ContentResolver> { on { loadThumbnail(any(), any(), anyOrNull()) } doReturn bitmap } private val scheduler = TestCoroutineScheduler() private val dispatcher = UnconfinedTestDispatcher(scheduler) private val scope = TestScope(dispatcher) @@ -211,8 +212,8 @@ class ImagePreviewImageLoaderTest { scope.runTest { val contentResolver = mock<ContentResolver> { - whenever(loadThumbnail(any(), any(), anyOrNull())) - .thenThrow(SecurityException("test")) + on { loadThumbnail(any(), any(), anyOrNull()) } doThrow + SecurityException("test") } val acquireCount = AtomicInteger() val releaseCount = AtomicInteger() @@ -298,13 +299,16 @@ class ImagePreviewImageLoaderTest { val pendingThumbnailCalls = ArrayDeque<CountDownLatch>() val contentResolver = mock<ContentResolver> { - whenever(loadThumbnail(any(), any(), anyOrNull())).thenAnswer { - val latch = CountDownLatch(1) - synchronized(pendingThumbnailCalls) { pendingThumbnailCalls.offer(latch) } - thumbnailCallsCdl.countDown() - assertTrue("Timeout waiting thumbnail calls", latch.await(1, SECONDS)) - bitmap - } + on { loadThumbnail(any(), any(), anyOrNull()) } doAnswer + { + val latch = CountDownLatch(1) + synchronized(pendingThumbnailCalls) { + pendingThumbnailCalls.offer(latch) + } + thumbnailCallsCdl.countDown() + assertTrue("Timeout waiting thumbnail calls", latch.await(1, SECONDS)) + bitmap + } } val name = "LoadImage" val maxSimultaneousRequests = 2 diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt index babfaaf5..a2fb9693 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt @@ -24,8 +24,6 @@ import android.net.Uri import android.platform.test.flag.junit.CheckFlagsRule import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.provider.DocumentsContract -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Truth.assertThat import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.CoroutineScope @@ -36,10 +34,12 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import org.mockito.Mockito.any -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) class PreviewDataProviderTest { diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt index 9a15f90a..60159160 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt @@ -24,8 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.intentresolver.ContentTypeHint import com.android.intentresolver.R -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.android.intentresolver.widget.ActionRow import com.google.common.truth.Truth.assertThat import java.util.function.Consumer @@ -34,6 +32,8 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class TextContentPreviewUiTest { @@ -56,8 +56,8 @@ class TextContentPreviewUiTest { private val imageLoader = mock<ImageLoader>() private val headlineGenerator = mock<HeadlineGenerator> { - whenever(getTextHeadline(text)).thenReturn(text) - whenever(getAlbumHeadline()).thenReturn(albumHeadline) + on { getTextHeadline(text) } doReturn text + on { getAlbumHeadline() } doReturn albumHeadline } private val testMetadataText: CharSequence = "Test metadata text" diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt index 98e6c381..21eb12ea 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt @@ -25,12 +25,11 @@ import androidx.annotation.IdRes import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.intentresolver.R -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.android.intentresolver.widget.ImagePreviewView.TransitionElementStatusCallback import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.takeWhile @@ -39,9 +38,11 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify private const val IMAGE_HEADLINE = "Image Headline" private const val VIDEO_HEADLINE = "Video Headline" @@ -49,17 +50,18 @@ private const val FILES_HEADLINE = "Files Headline" @RunWith(AndroidJUnit4::class) class UnifiedContentPreviewUiTest { + @OptIn(ExperimentalCoroutinesApi::class) private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher()) private val actionFactory = mock<ChooserContentPreviewUi.ActionFactory> { - whenever(createCustomActions()).thenReturn(emptyList()) + on { createCustomActions() } doReturn emptyList() } private val imageLoader = mock<ImageLoader>() private val headlineGenerator = mock<HeadlineGenerator> { - whenever(getImagesHeadline(anyInt())).thenReturn(IMAGE_HEADLINE) - whenever(getVideosHeadline(anyInt())).thenReturn(VIDEO_HEADLINE) - whenever(getFilesHeadline(anyInt())).thenReturn(FILES_HEADLINE) + on { getImagesHeadline(any()) } doReturn IMAGE_HEADLINE + on { getVideosHeadline(any()) } doReturn VIDEO_HEADLINE + on { getFilesHeadline(any()) } doReturn FILES_HEADLINE } private val testMetadataText: CharSequence = "Test metadata text" diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/UriMetadataReaderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/UriMetadataReaderTest.kt index 07f3a3f2..74f1e59d 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/UriMetadataReaderTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/UriMetadataReaderTest.kt @@ -21,13 +21,13 @@ import android.database.MatrixCursor import android.media.MediaMetadata import android.net.Uri import android.provider.DocumentsContract -import com.android.intentresolver.any -import com.android.intentresolver.anyOrNull -import com.android.intentresolver.eq -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Truth.assertWithMessage import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever class UriMetadataReaderTest { private val uri = Uri.parse("content://org.pkg.app/item") diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt index 55b32509..91bbd151 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt @@ -41,14 +41,9 @@ import android.service.chooser.ChooserTarget import android.service.chooser.Flags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.android.intentresolver.any -import com.android.intentresolver.argumentCaptor -import com.android.intentresolver.capture import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate.Absent import com.android.intentresolver.inject.FakeChooserServiceFlags -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Correspondence import com.google.common.truth.Correspondence.BinaryPredicate import com.google.common.truth.Truth.assertThat @@ -57,8 +52,13 @@ import java.lang.IllegalArgumentException import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.capture +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SelectionChangeCallbackImplTest { @@ -97,25 +97,24 @@ class SelectionChangeCallbackImplTest { val extraCaptor = argumentCaptor<Bundle>() verify(contentResolver, times(1)) .call( - capture(authorityCaptor), - capture(methodCaptor), - capture(argCaptor), - capture(extraCaptor) + authorityCaptor.capture(), + methodCaptor.capture(), + argCaptor.capture(), + extraCaptor.capture() ) assertWithMessage("Wrong additional content provider authority") - .that(authorityCaptor.value) + .that(authorityCaptor.firstValue) .isEqualTo(uri.authority) assertWithMessage("Wrong additional content provider #call() method name") - .that(methodCaptor.value) + .that(methodCaptor.firstValue) .isEqualTo(ON_SELECTION_CHANGED) assertWithMessage("Wrong additional content provider argument value") - .that(argCaptor.value) + .that(argCaptor.firstValue) .isEqualTo(uri.toString()) - val extraBundle = extraCaptor.value + val extraBundle = extraCaptor.firstValue assertWithMessage("Additional content provider #call() should have a non-null extras arg.") .that(extraBundle) .isNotNull() - requireNotNull(extraBundle) val argChooserIntent = extraBundle.getParcelable(EXTRA_INTENT, Intent::class.java) assertWithMessage("#call() extras arg. should contain Intent#EXTRA_INTENT") .that(argChooserIntent) diff --git a/tests/unit/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt b/tests/unit/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt index 4c05dfb1..084d12e6 100644 --- a/tests/unit/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt +++ b/tests/unit/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt @@ -17,9 +17,9 @@ package com.android.intentresolver.emptystate import com.android.intentresolver.ResolverListAdapter -import com.android.intentresolver.mock import com.google.common.truth.Truth.assertThat import org.junit.Test +import org.mockito.kotlin.mock class CompositeEmptyStateProviderTest { val listAdapter = mock<ResolverListAdapter>() diff --git a/tests/unit/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProviderTest.kt b/tests/unit/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProviderTest.kt index fe3e844b..135ac064 100644 --- a/tests/unit/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProviderTest.kt +++ b/tests/unit/src/com/android/intentresolver/emptystate/NoCrossProfileEmptyStateProviderTest.kt @@ -20,34 +20,35 @@ import android.content.Intent import com.android.intentresolver.ProfileHelper import com.android.intentresolver.ResolverListAdapter import com.android.intentresolver.annotation.JavaInterop +import com.android.intentresolver.data.repository.DevicePolicyResources import com.android.intentresolver.data.repository.FakeUserRepository import com.android.intentresolver.domain.interactor.UserInteractor import com.android.intentresolver.inject.FakeIntentResolverFlags import com.android.intentresolver.shared.model.User import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import org.junit.Test -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyList +import org.mockito.Mockito.never +import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock -import org.mockito.kotlin.never import org.mockito.kotlin.same import org.mockito.kotlin.times import org.mockito.kotlin.verify +import org.mockito.verification.VerificationMode @OptIn(JavaInterop::class) class NoCrossProfileEmptyStateProviderTest { private val personalUser = User(0, User.Role.PERSONAL) private val workUser = User(10, User.Role.WORK) + private val privateUser = User(11, User.Role.PRIVATE) private val flags = FakeIntentResolverFlags() - private val personalBlocker = mock<EmptyState>() - private val workBlocker = mock<EmptyState>() - private val userRepository = FakeUserRepository(listOf(personalUser, workUser)) + private val userRepository = FakeUserRepository(listOf(personalUser, workUser, privateUser)) private val personalIntents = listOf(Intent("PERSONAL")) private val personalListAdapter = @@ -61,96 +62,169 @@ class NoCrossProfileEmptyStateProviderTest { on { userHandle } doReturn workUser.handle on { intents } doReturn workIntents } + private val privateIntents = listOf(Intent("PRIVATE")) + private val privateListAdapter = + mock<ResolverListAdapter> { + on { userHandle } doReturn privateUser.handle + on { intents } doReturn privateIntents + } + + private val devicePolicyResources = + mock<DevicePolicyResources> { + on { crossProfileBlocked } doReturn "Cross profile blocked" + on { toPersonalBlockedByPolicyMessage(any()) } doReturn "Blocked to Personal" + on { toWorkBlockedByPolicyMessage(any()) } doReturn "Blocked to Work" + on { toPrivateBlockedByPolicyMessage(any()) } doReturn "Blocked to Private" + } - // Pretend that no intent can ever be forwarded - val crossProfileIntentsChecker = + // If asked, no intent can ever be forwarded between any pair of users. + private val crossProfileIntentsChecker = mock<CrossProfileIntentsChecker> { on { hasCrossProfileIntents( - /* intents = */ anyList(), - /* source = */ anyInt(), - /* target = */ anyInt() + /* intents = */ any(), + /* source = */ any(), + /* target = */ any() ) - } doReturn false + } doReturn false /* Never allow */ } - private val sourceUserId = argumentCaptor<Int>() - private val targetUserId = argumentCaptor<Int>() @Test - fun testPersonalToWork() { - val userInteractor = UserInteractor(userRepository, launchedAs = personalUser.handle) - - val profileHelper = - ProfileHelper( - userInteractor, - CoroutineScope(Dispatchers.Unconfined), - Dispatchers.Unconfined, - flags - ) + fun verifyTestSetup() { + assertThat(workListAdapter.userHandle).isEqualTo(workUser.handle) + assertThat(personalListAdapter.userHandle).isEqualTo(personalUser.handle) + assertThat(privateListAdapter.userHandle).isEqualTo(privateUser.handle) + } + + @Test + fun sameProfilePermitted() { + val profileHelper = createProfileHelper(launchedAs = workUser) val provider = NoCrossProfileEmptyStateProvider( - /* profileHelper = */ profileHelper, - /* noWorkToPersonalEmptyState = */ personalBlocker, - /* noPersonalToWorkEmptyState = */ workBlocker, - /* crossProfileIntentsChecker = */ crossProfileIntentsChecker + profileHelper, + devicePolicyResources, + crossProfileIntentsChecker, + /* isShare = */ true ) - // Personal to personal, not blocked - assertThat(provider.getEmptyState(personalListAdapter)).isNull() - // Not called because sourceUser == targetUser - verify(crossProfileIntentsChecker, never()) - .hasCrossProfileIntents(anyList(), anyInt(), anyInt()) - - // Personal to work, blocked - assertThat(provider.getEmptyState(workListAdapter)).isSameInstanceAs(workBlocker) - - verify(crossProfileIntentsChecker, times(1)) - .hasCrossProfileIntents( - same(workIntents), - sourceUserId.capture(), - targetUserId.capture() + // Work to work, not blocked + assertThat(provider.getEmptyState(workListAdapter)).isNull() + + crossProfileIntentsChecker.verifyCalled(never()) + } + + @Test + fun testPersonalToWork() { + val profileHelper = createProfileHelper(launchedAs = personalUser) + + val provider = + NoCrossProfileEmptyStateProvider( + profileHelper, + devicePolicyResources, + crossProfileIntentsChecker, + /* isShare = */ true ) - assertThat(sourceUserId.firstValue).isEqualTo(personalUser.id) - assertThat(targetUserId.firstValue).isEqualTo(workUser.id) + + val result = provider.getEmptyState(workListAdapter) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo("Cross profile blocked") + assertThat(result?.subtitle).isEqualTo("Blocked to Work") + + crossProfileIntentsChecker.verifyCalled(times(1), workIntents, personalUser, workUser) } @Test fun testWorkToPersonal() { - val userInteractor = UserInteractor(userRepository, launchedAs = workUser.handle) - - val profileHelper = - ProfileHelper( - userInteractor, - CoroutineScope(Dispatchers.Unconfined), - Dispatchers.Unconfined, - flags + val profileHelper = createProfileHelper(launchedAs = workUser) + + val provider = + NoCrossProfileEmptyStateProvider( + profileHelper, + devicePolicyResources, + crossProfileIntentsChecker, + /* isShare = */ true ) + val result = provider.getEmptyState(personalListAdapter) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo("Cross profile blocked") + assertThat(result?.subtitle).isEqualTo("Blocked to Personal") + + crossProfileIntentsChecker.verifyCalled(times(1), personalIntents, workUser, personalUser) + } + + @Test + fun testWorkToPrivate() { + val profileHelper = createProfileHelper(launchedAs = workUser) + val provider = NoCrossProfileEmptyStateProvider( - /* profileHelper = */ profileHelper, - /* noWorkToPersonalEmptyState = */ personalBlocker, - /* noPersonalToWorkEmptyState = */ workBlocker, - /* crossProfileIntentsChecker = */ crossProfileIntentsChecker + profileHelper, + devicePolicyResources, + crossProfileIntentsChecker, + /* isShare = */ true ) - // Work to work, not blocked - assertThat(provider.getEmptyState(workListAdapter)).isNull() - // Not called because sourceUser == targetUser - verify(crossProfileIntentsChecker, never()) - .hasCrossProfileIntents(anyList(), anyInt(), anyInt()) - - // Work to personal, blocked - assertThat(provider.getEmptyState(personalListAdapter)).isSameInstanceAs(personalBlocker) - - verify(crossProfileIntentsChecker, times(1)) - .hasCrossProfileIntents( - same(personalIntents), - sourceUserId.capture(), - targetUserId.capture() + val result = provider.getEmptyState(privateListAdapter) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo("Cross profile blocked") + assertThat(result?.subtitle).isEqualTo("Blocked to Private") + + // effective target user is personalUser due to "delegate from parent" + crossProfileIntentsChecker.verifyCalled(times(1), privateIntents, workUser, personalUser) + } + + @Test + fun testPrivateToPersonal() { + val profileHelper = createProfileHelper(launchedAs = privateUser) + + val provider = + NoCrossProfileEmptyStateProvider( + profileHelper, + devicePolicyResources, + crossProfileIntentsChecker, + /* isShare = */ true ) - assertThat(sourceUserId.firstValue).isEqualTo(workUser.id) - assertThat(targetUserId.firstValue).isEqualTo(personalUser.id) + + // Private -> Personal is always allowed: + // Private delegates to the parent profile for policy; so personal->personal is allowed. + assertThat(provider.getEmptyState(personalListAdapter)).isNull() + + crossProfileIntentsChecker.verifyCalled(never()) + } + + private fun createProfileHelper(launchedAs: User): ProfileHelper { + val userInteractor = UserInteractor(userRepository, launchedAs = launchedAs.handle) + + return ProfileHelper( + userInteractor, + CoroutineScope(Dispatchers.Unconfined), + Dispatchers.Unconfined, + flags + ) + } + + private fun CrossProfileIntentsChecker.verifyCalled( + mode: VerificationMode, + list: List<Intent>? = null, + sourceUser: User? = null, + targetUser: User? = null, + ) { + val sourceUserId = argumentCaptor<Int>() + val targetUserId = argumentCaptor<Int>() + + verify(this, mode) + .hasCrossProfileIntents(same(list), sourceUserId.capture(), targetUserId.capture()) + sourceUser?.apply { + assertWithMessage("hasCrossProfileIntents: source") + .that(sourceUserId.firstValue) + .isEqualTo(id) + } + targetUser?.apply { + assertWithMessage("hasCrossProfileIntents: target") + .that(targetUserId.firstValue) + .isEqualTo(id) + } } } diff --git a/tests/unit/src/com/android/intentresolver/profiles/MultiProfilePagerAdapterTest.kt b/tests/unit/src/com/android/intentresolver/profiles/MultiProfilePagerAdapterTest.kt index edeb5c8c..8eafa0b5 100644 --- a/tests/unit/src/com/android/intentresolver/profiles/MultiProfilePagerAdapterTest.kt +++ b/tests/unit/src/com/android/intentresolver/profiles/MultiProfilePagerAdapterTest.kt @@ -25,15 +25,15 @@ import androidx.test.platform.app.InstrumentationRegistry import com.android.intentresolver.R import com.android.intentresolver.ResolverListAdapter import com.android.intentresolver.emptystate.EmptyStateProvider -import com.android.intentresolver.mock import com.android.intentresolver.profiles.MultiProfilePagerAdapter.PROFILE_PERSONAL import com.android.intentresolver.profiles.MultiProfilePagerAdapter.PROFILE_WORK -import com.android.intentresolver.whenever import com.google.common.collect.ImmutableList import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.function.Supplier import org.junit.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock class MultiProfilePagerAdapterTest { private val PERSONAL_USER_HANDLE = UserHandle.of(10) @@ -48,7 +48,7 @@ class MultiProfilePagerAdapterTest { @Test fun testSinglePageProfileAdapter() { val personalListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE } val pagerAdapter = MultiProfilePagerAdapter( { listAdapter: ResolverListAdapter -> listAdapter }, @@ -87,9 +87,9 @@ class MultiProfilePagerAdapterTest { @Test fun testTwoProfilePagerAdapter() { val personalListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE } val workListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn WORK_USER_HANDLE } val pagerAdapter = MultiProfilePagerAdapter( { listAdapter: ResolverListAdapter -> listAdapter }, @@ -134,9 +134,9 @@ class MultiProfilePagerAdapterTest { @Test fun testTwoProfilePagerAdapter_workIsDefault() { val personalListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE } val workListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn WORK_USER_HANDLE } val pagerAdapter = MultiProfilePagerAdapter( { listAdapter: ResolverListAdapter -> listAdapter }, @@ -179,7 +179,7 @@ class MultiProfilePagerAdapterTest { @Test fun testBottomPaddingDelegate_default() { val personalListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE } val pagerAdapter = MultiProfilePagerAdapter( { listAdapter: ResolverListAdapter -> listAdapter }, @@ -204,9 +204,9 @@ class MultiProfilePagerAdapterTest { { Optional.empty() } ) val container = - pagerAdapter - .getActiveEmptyStateView() - .requireViewById<View>(com.android.internal.R.id.resolver_empty_state_container) + pagerAdapter.activeEmptyStateView.requireViewById<View>( + com.android.internal.R.id.resolver_empty_state_container + ) container.setPadding(1, 2, 3, 4) pagerAdapter.setupContainerPadding() assertThat(container.paddingLeft).isEqualTo(1) @@ -218,7 +218,7 @@ class MultiProfilePagerAdapterTest { @Test fun testBottomPaddingDelegate_override() { val personalListAdapter = - mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) } + mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE } val pagerAdapter = MultiProfilePagerAdapter( { listAdapter: ResolverListAdapter -> listAdapter }, @@ -243,9 +243,9 @@ class MultiProfilePagerAdapterTest { { Optional.of(42) } ) val container = - pagerAdapter - .getActiveEmptyStateView() - .requireViewById<View>(com.android.internal.R.id.resolver_empty_state_container) + pagerAdapter.activeEmptyStateView.requireViewById<View>( + com.android.internal.R.id.resolver_empty_state_container + ) container.setPadding(1, 2, 3, 4) pagerAdapter.setupContainerPadding() assertThat(container.paddingLeft).isEqualTo(1) @@ -261,13 +261,13 @@ class MultiProfilePagerAdapterTest { // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider? val personalListAdapter = mock<ResolverListAdapter> { - whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) - whenever(getUnfilteredCount()).thenReturn(1) + on { userHandle } doReturn PERSONAL_USER_HANDLE + on { unfilteredCount } doReturn 1 } val workListAdapter = mock<ResolverListAdapter> { - whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) - whenever(getUnfilteredCount()).thenReturn(1) + on { userHandle } doReturn WORK_USER_HANDLE + on { unfilteredCount } doReturn 1 } val pagerAdapter = MultiProfilePagerAdapter( @@ -304,13 +304,13 @@ class MultiProfilePagerAdapterTest { // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider? val personalListAdapter = mock<ResolverListAdapter> { - whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) - whenever(getUnfilteredCount()).thenReturn(1) + on { userHandle } doReturn PERSONAL_USER_HANDLE + on { unfilteredCount } doReturn 1 } val workListAdapter = mock<ResolverListAdapter> { - whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) - whenever(getUnfilteredCount()).thenReturn(1) + on { userHandle } doReturn WORK_USER_HANDLE + on { unfilteredCount } doReturn 1 } val pagerAdapter = MultiProfilePagerAdapter( diff --git a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt index 4eeae872..fbdc062b 100644 --- a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt +++ b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt @@ -27,15 +27,10 @@ import android.content.pm.ShortcutManager import android.os.UserHandle import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.intentresolver.any -import com.android.intentresolver.argumentCaptor -import com.android.intentresolver.capture import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.createAppTarget import com.android.intentresolver.createShareShortcutInfo import com.android.intentresolver.createShortcutInfo -import com.android.intentresolver.mock -import com.android.intentresolver.whenever import com.google.common.truth.Truth.assertWithMessage import java.util.function.Consumer import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -48,11 +43,15 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -64,19 +63,19 @@ class ShortcutLoaderTest { } private val pm = mock<PackageManager> { - whenever(getApplicationInfo(any(), any<ApplicationInfoFlags>())).thenReturn(appInfo) + on { getApplicationInfo(any(), any<ApplicationInfoFlags>()) } doReturn appInfo } private val userManager = mock<UserManager> { - whenever(isUserRunning(any<UserHandle>())).thenReturn(true) - whenever(isUserUnlocked(any<UserHandle>())).thenReturn(true) - whenever(isQuietModeEnabled(any<UserHandle>())).thenReturn(false) + on { isUserRunning(any<UserHandle>()) } doReturn true + on { isUserUnlocked(any<UserHandle>()) } doReturn true + on { isQuietModeEnabled(any<UserHandle>()) } doReturn false } private val context = mock<Context> { - whenever(packageManager).thenReturn(pm) - whenever(createContextAsUser(any(), anyInt())).thenReturn(this) - whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager) + on { packageManager } doReturn pm + on { createContextAsUser(any(), any()) } doReturn mock + on { getSystemService(Context.USER_SERVICE) } doReturn userManager } private val scheduler = TestCoroutineScheduler() private val dispatcher = UnconfinedTestDispatcher(scheduler) @@ -86,7 +85,7 @@ class ShortcutLoaderTest { private val callback = mock<Consumer<ShortcutLoader.Result>>() private val componentName = ComponentName("pkg", "Class") private val appTarget = - mock<DisplayResolveInfo> { whenever(resolvedComponentName).thenReturn(componentName) } + mock<DisplayResolveInfo> { on { resolvedComponentName } doReturn componentName } private val appTargets = arrayOf(appTarget) private val matchingShortcutInfo = createShortcutInfo("id-0", componentName, 1) @@ -119,13 +118,13 @@ class ShortcutLoaderTest { ) val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>() verify(appPredictor, atLeastOnce()) - .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor)) - appPredictorCallbackCaptor.value.onTargetsAvailable(shortcuts) + .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture()) + appPredictorCallbackCaptor.firstValue.onTargetsAvailable(shortcuts) val resultCaptor = argumentCaptor<ShortcutLoader.Result>() - verify(callback, times(1)).accept(capture(resultCaptor)) + verify(callback, times(1)).accept(resultCaptor.capture()) - val result = resultCaptor.value + val result = resultCaptor.firstValue assertTrue("An app predictor result is expected", result.isFromAppPredictor) assertArrayEquals( "Wrong input app targets in the result", @@ -159,7 +158,7 @@ class ShortcutLoaderTest { ) val shortcutManager = mock<ShortcutManager> { - whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult) + on { getShareTargets(intentFilter) } doReturn shortcutManagerResult } whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) val testSubject = @@ -177,9 +176,9 @@ class ShortcutLoaderTest { testSubject.updateAppTargets(appTargets) val resultCaptor = argumentCaptor<ShortcutLoader.Result>() - verify(callback, times(1)).accept(capture(resultCaptor)) + verify(callback, times(1)).accept(resultCaptor.capture()) - val result = resultCaptor.value + val result = resultCaptor.firstValue assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor) assertArrayEquals( "Wrong input app targets in the result", @@ -212,7 +211,7 @@ class ShortcutLoaderTest { ) val shortcutManager = mock<ShortcutManager> { - whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult) + on { getShareTargets(intentFilter) } doReturn (shortcutManagerResult) } whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) val testSubject = @@ -232,13 +231,13 @@ class ShortcutLoaderTest { verify(appPredictor, times(1)).requestPredictionUpdate() val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>() verify(appPredictor, times(1)) - .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor)) - appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList()) + .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture()) + appPredictorCallbackCaptor.firstValue.onTargetsAvailable(emptyList()) val resultCaptor = argumentCaptor<ShortcutLoader.Result>() - verify(callback, times(1)).accept(capture(resultCaptor)) + verify(callback, times(1)).accept(resultCaptor.capture()) - val result = resultCaptor.value + val result = resultCaptor.firstValue assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor) assertArrayEquals( "Wrong input app targets in the result", @@ -271,7 +270,7 @@ class ShortcutLoaderTest { ) val shortcutManager = mock<ShortcutManager> { - whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult) + on { getShareTargets(intentFilter) } doReturn shortcutManagerResult } whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) whenever(appPredictor.requestPredictionUpdate()) @@ -293,9 +292,9 @@ class ShortcutLoaderTest { verify(appPredictor, times(1)).requestPredictionUpdate() val resultCaptor = argumentCaptor<ShortcutLoader.Result>() - verify(callback, times(1)).accept(capture(resultCaptor)) + verify(callback, times(1)).accept(resultCaptor.capture()) - val result = resultCaptor.value + val result = resultCaptor.firstValue assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor) assertArrayEquals( "Wrong input app targets in the result", @@ -346,7 +345,7 @@ class ShortcutLoaderTest { ) val shortcutManager = mock<ShortcutManager> { - whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult) + on { getShareTargets(intentFilter) } doReturn shortcutManagerResult } whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) val testSubject = @@ -417,14 +416,14 @@ class ShortcutLoaderTest { verify(appPredictor, times(1)).requestPredictionUpdate() val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>() verify(appPredictor, times(1)) - .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor)) - appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList()) + .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture()) + appPredictorCallbackCaptor.firstValue.onTargetsAvailable(emptyList()) verify(shortcutManager, never()).getShareTargets(any()) val resultCaptor = argumentCaptor<ShortcutLoader.Result>() - verify(callback, times(1)).accept(capture(resultCaptor)) + verify(callback, times(1)).accept(resultCaptor.capture()) - val result = resultCaptor.value + val result = resultCaptor.firstValue assertWithMessage("A ShortcutManager result is expected") .that(result.isFromAppPredictor) .isFalse() diff --git a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt index e0de005d..ce6ef477 100644 --- a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt +++ b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt @@ -32,9 +32,9 @@ private const val PACKAGE = "org.package" class ShortcutToChooserTargetConverterTest { private val testSubject = ShortcutToChooserTargetConverter() - private val ranks = arrayOf(3 ,7, 1 ,3) - private val shortcuts = ranks - .foldIndexed(ArrayList<ShareShortcutInfo>(ranks.size)) { i, acc, rank -> + private val ranks = arrayOf(3, 7, 1, 3) + private val shortcuts = + ranks.foldIndexed(ArrayList<ShareShortcutInfo>(ranks.size)) { i, acc, rank -> val id = i + 1 acc.add( createShareShortcutInfo( @@ -54,13 +54,14 @@ class ShortcutToChooserTargetConverterTest { val appTargetCache = HashMap<ChooserTarget, AppTarget>() val shortcutInfoCache = HashMap<ChooserTarget, ShortcutInfo>() - var chooserTargets = testSubject.convertToChooserTarget( - shortcuts, - shortcuts, - appTargets, - appTargetCache, - shortcutInfoCache, - ) + var chooserTargets = + testSubject.convertToChooserTarget( + shortcuts, + shortcuts, + appTargets, + appTargetCache, + shortcutInfoCache, + ) assertCorrectShortcutToChooserTargetConversion( shortcuts, @@ -77,13 +78,14 @@ class ShortcutToChooserTargetConverterTest { appTargetCache.clear() shortcutInfoCache.clear() - chooserTargets = testSubject.convertToChooserTarget( - subset, - shortcuts, - appTargets, - appTargetCache, - shortcutInfoCache, - ) + chooserTargets = + testSubject.convertToChooserTarget( + subset, + shortcuts, + appTargets, + appTargetCache, + shortcutInfoCache, + ) assertCorrectShortcutToChooserTargetConversion( shortcuts, @@ -102,17 +104,20 @@ class ShortcutToChooserTargetConverterTest { val expectedScoreAllShortcuts = floatArrayOf(1.0f, 0.99f, 0.99f, 0.98f) val shortcutInfoCache = HashMap<ChooserTarget, ShortcutInfo>() - var chooserTargets = testSubject.convertToChooserTarget( - shortcuts, - shortcuts, - null, - null, - shortcutInfoCache, - ) + var chooserTargets = + testSubject.convertToChooserTarget( + shortcuts, + shortcuts, + null, + null, + shortcutInfoCache, + ) assertCorrectShortcutToChooserTargetConversion( - shortcuts, chooserTargets, - expectedOrderAllShortcuts, expectedScoreAllShortcuts + shortcuts, + chooserTargets, + expectedOrderAllShortcuts, + expectedScoreAllShortcuts ) assertShortcutInfoCache(chooserTargets, shortcutInfoCache) @@ -124,17 +129,20 @@ class ShortcutToChooserTargetConverterTest { val expectedScoreSubset = floatArrayOf(1.0f, 0.99f, 0.98f) shortcutInfoCache.clear() - chooserTargets = testSubject.convertToChooserTarget( - subset, - shortcuts, - null, - null, - shortcutInfoCache, - ) + chooserTargets = + testSubject.convertToChooserTarget( + subset, + shortcuts, + null, + null, + shortcutInfoCache, + ) assertCorrectShortcutToChooserTargetConversion( - shortcuts, chooserTargets, - expectedOrderSubset, expectedScoreSubset + shortcuts, + chooserTargets, + expectedOrderSubset, + expectedScoreSubset ) assertShortcutInfoCache(chooserTargets, shortcutInfoCache) } @@ -158,7 +166,8 @@ class ShortcutToChooserTargetConverterTest { } private fun assertAppTargetCache( - chooserTargets: List<ChooserTarget>, cache: Map<ChooserTarget, AppTarget> + chooserTargets: List<ChooserTarget>, + cache: Map<ChooserTarget, AppTarget> ) { for (ct in chooserTargets) { val target = cache[ct] @@ -167,7 +176,8 @@ class ShortcutToChooserTargetConverterTest { } private fun assertShortcutInfoCache( - chooserTargets: List<ChooserTarget>, cache: Map<ChooserTarget, ShortcutInfo> + chooserTargets: List<ChooserTarget>, + cache: Map<ChooserTarget, ShortcutInfo> ) { for (ct in chooserTargets) { val si = cache[ct] |