diff options
Diffstat (limited to 'java')
120 files changed, 876 insertions, 252 deletions
diff --git a/java/res/layout/chooser_action_view.xml b/java/res/layout/chooser_action_view.xml index 6177821a..b4859258 100644 --- a/java/res/layout/chooser_action_view.xml +++ b/java/res/layout/chooser_action_view.xml @@ -14,7 +14,7 @@ ~ limitations under the License --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" +<Button xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" style="?android:attr/borderlessButtonStyle" android:background="@drawable/chooser_action_button_bg" diff --git a/java/res/layout/chooser_grid_item_hover.xml b/java/res/layout/chooser_grid_item_hover.xml new file mode 100644 index 00000000..05206065 --- /dev/null +++ b/java/res/layout/chooser_grid_item_hover.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.android.intentresolver.widget.ChooserTargetItemView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@androidprv:id/item" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="100dp" + android:gravity="top|center_horizontal" + android:paddingVertical="1dp" + android:paddingHorizontal="4dp" + android:focusable="true" + android:defaultFocusHighlightEnabled="false" + app:focusOutlineWidth="@dimen/chooser_item_focus_outline_width" + app:focusOutlineCornerRadius="@dimen/chooser_item_focus_outline_corner_radius" + app:focusOutlineColor="?androidprv:attr/materialColorSecondaryFixed" + app:focusInnerOutlineColor="?androidprv:attr/materialColorOnSecondaryFixedVariant"> + + <ImageView android:id="@android:id/icon" + android:layout_width="@dimen/chooser_icon_width_with_padding" + android:layout_height="@dimen/chooser_icon_height_with_padding" + android:paddingHorizontal="@dimen/chooser_icon_horizontal_padding" + android:paddingBottom="@dimen/chooser_icon_vertical_padding" + android:scaleType="fitCenter" /> + + <!-- NOTE: for id/text1 and id/text2 below set the width to match parent as a workaround for + b/269395540 i.e. prevent views bounds change during a transition animation. It does not + affect pinned views as we change their layout parameters programmatically (but that's even + more narrow possibility and it's not clear if the root cause or the bug would affect it). + --> + <!-- App name or Direct Share target name, DS set to 2 lines --> + <com.android.intentresolver.widget.BadgeTextView + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?androidprv:attr/materialColorOnSurface" + android:textSize="@dimen/chooser_grid_target_name_text_size" + android:maxLines="1" + android:ellipsize="end" /> + + <!-- Activity name if set, gone for Direct Share targets --> + <TextView android:id="@android:id/text2" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textSize="@dimen/chooser_grid_activity_name_text_size" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:lines="1" + android:gravity="top|center_horizontal" + android:ellipsize="end"/> + +</com.android.intentresolver.widget.ChooserTargetItemView> diff --git a/java/res/layout/chooser_grid_preview_image.xml b/java/res/layout/chooser_grid_preview_image.xml index 4745e04c..f1aad727 100644 --- a/java/res/layout/chooser_grid_preview_image.xml +++ b/java/res/layout/chooser_grid_preview_image.xml @@ -41,7 +41,8 @@ android:layout_gravity="center_horizontal" android:layout_marginBottom="8dp" app:itemInnerSpacing="3dp" - app:itemOuterSpacing="@dimen/chooser_edge_margin_normal"/> + app:itemOuterSpacing="@dimen/chooser_edge_margin_normal" + app:editButtonRoleDescription="@string/role_description_button"/> <include layout="@layout/chooser_action_row"/> </LinearLayout> diff --git a/java/res/layout/chooser_headline_row.xml b/java/res/layout/chooser_headline_row.xml index 01be653f..4e19249b 100644 --- a/java/res/layout/chooser_headline_row.xml +++ b/java/res/layout/chooser_headline_row.xml @@ -60,7 +60,7 @@ app:barrierDirection="start" app:constraint_referenced_ids="reselection_action,include_text_action" /> - <TextView + <Button android:id="@+id/reselection_action" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 55d84dfa..f24528a0 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Kiesbare prent"</string> <string name="selectable_video" msgid="1271768647699300826">"Kiesbare video"</string> <string name="selectable_item" msgid="7557320816744205280">"Kiesbare item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Knoppie"</string> </resources> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index a7b5922b..d46f88d1 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ሊመረጥ የሚችል ምስል"</string> <string name="selectable_video" msgid="1271768647699300826">"ሊመረጥ የሚችል ቪድዮ"</string> <string name="selectable_item" msgid="7557320816744205280">"ሊመረጥ የሚችል ንጥል"</string> + <string name="role_description_button" msgid="4537198530568333649">"አዝራር"</string> </resources> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 49769c57..278e03f2 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"صورة يمكن اختيارها"</string> <string name="selectable_video" msgid="1271768647699300826">"فيديو يمكن اختياره"</string> <string name="selectable_item" msgid="7557320816744205280">"عنصر يمكن اختياره"</string> + <string name="role_description_button" msgid="4537198530568333649">"زرّ"</string> </resources> diff --git a/java/res/values-as/strings.xml b/java/res/values-as/strings.xml index 1983e4fe..2177c527 100644 --- a/java/res/values-as/strings.xml +++ b/java/res/values-as/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"বাছনি কৰিব পৰা প্ৰতিচ্ছবি"</string> <string name="selectable_video" msgid="1271768647699300826">"বাছনি কৰিব পৰা ভিডিঅ’"</string> <string name="selectable_item" msgid="7557320816744205280">"বাছনি কৰিব পৰা বস্তু"</string> + <string name="role_description_button" msgid="4537198530568333649">"বুটাম"</string> </resources> diff --git a/java/res/values-az/strings.xml b/java/res/values-az/strings.xml index c5674b86..93086938 100644 --- a/java/res/values-az/strings.xml +++ b/java/res/values-az/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Seçilə bilən şəkil"</string> <string name="selectable_video" msgid="1271768647699300826">"Seçilə bilən video"</string> <string name="selectable_item" msgid="7557320816744205280">"Seçilə bilən element"</string> + <string name="role_description_button" msgid="4537198530568333649">"Düymə"</string> </resources> diff --git a/java/res/values-b+sr+Latn/strings.xml b/java/res/values-b+sr+Latn/strings.xml index 6d9dbd87..86fc1854 100644 --- a/java/res/values-b+sr+Latn/strings.xml +++ b/java/res/values-b+sr+Latn/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Slika koja može da se izabere"</string> <string name="selectable_video" msgid="1271768647699300826">"Video koji može da se izabere"</string> <string name="selectable_item" msgid="7557320816744205280">"Stavka koja može da se izabere"</string> + <string name="role_description_button" msgid="4537198530568333649">"Dugme"</string> </resources> diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml index 2724855b..97ca27d3 100644 --- a/java/res/values-be/strings.xml +++ b/java/res/values-be/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Відарыс, які можна выбраць"</string> <string name="selectable_video" msgid="1271768647699300826">"Відэа, якое можна выбраць"</string> <string name="selectable_item" msgid="7557320816744205280">"Элемент, які можна выбраць"</string> + <string name="role_description_button" msgid="4537198530568333649">"Кнопка"</string> </resources> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 450712b1..3cec0cdf 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{+ # файл}other{+ # файла}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{+ още # файл}other{+ още # файла}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Текстът се споделя"</string> - <string name="sharing_link" msgid="2307694372813942916">"Връзката се споделя"</string> + <string name="sharing_link" msgid="2307694372813942916">"Споделяне на връзката"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Изображението се споделя}other{# изображения се споделят}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Видеоклипът се споделя}other{# видеоклипа се споделят}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{# файл се споделя}other{# файла се споделят}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Избираемо изображение"</string> <string name="selectable_video" msgid="1271768647699300826">"Избираем видеоклип"</string> <string name="selectable_item" msgid="7557320816744205280">"Избираем елемент"</string> + <string name="role_description_button" msgid="4537198530568333649">"Бутон"</string> </resources> diff --git a/java/res/values-bn/strings.xml b/java/res/values-bn/strings.xml index 2d33eb29..ea524006 100644 --- a/java/res/values-bn/strings.xml +++ b/java/res/values-bn/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{আরও #টি ফাইল}one{আরও #টি ফাইল}other{আরও #টি ফাইল}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{আরও #টি ফাইল}one{আরও #টি ফাইল}other{আরও #টি ফাইল}}"</string> <string name="sharing_text" msgid="8137537443603304062">"টেক্সট শেয়ার করা হচ্ছে"</string> - <string name="sharing_link" msgid="2307694372813942916">"শেয়ার করা লিঙ্ক"</string> + <string name="sharing_link" msgid="2307694372813942916">"শেয়ার করার জন্য লিঙ্ক"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{ছবি শেয়ার করা হচ্ছে}one{#টি ছবি শেয়ার করা হচ্ছে}other{#টি ছবি শেয়ার করা হচ্ছে}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{ভিডিও শেয়ার করা হচ্ছে}one{#টি ভিডিও শেয়ার করা হচ্ছে}other{#টি ভিডিও শেয়ার করা হচ্ছে}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{#টি ফাইল শেয়ার করা হচ্ছে}one{#টি ফাইল শেয়ার করা হচ্ছে}other{#টি ফাইল শেয়ার করা হচ্ছে}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"বেছে নেওয়া যাবে এমন ছবি"</string> <string name="selectable_video" msgid="1271768647699300826">"বেছে নেওয়া যাবে এমন ভিডিও"</string> <string name="selectable_item" msgid="7557320816744205280">"বেছে নেওয়া যাবে এমন আইটেম"</string> + <string name="role_description_button" msgid="4537198530568333649">"বোতাম"</string> </resources> diff --git a/java/res/values-bs/strings.xml b/java/res/values-bs/strings.xml index 10335fab..ddf3119b 100644 --- a/java/res/values-bs/strings.xml +++ b/java/res/values-bs/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Slika koju je moguće odabrati"</string> <string name="selectable_video" msgid="1271768647699300826">"Videozapis koji je moguće odabrati"</string> <string name="selectable_item" msgid="7557320816744205280">"Stavka koju je moguće odabrati"</string> + <string name="role_description_button" msgid="4537198530568333649">"Dugme"</string> </resources> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index 11029365..48d7138f 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imatge seleccionable"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo seleccionable"</string> <string name="selectable_item" msgid="7557320816744205280">"Element seleccionable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botó"</string> </resources> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 0ce7e140..151e2147 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Vybratelný obrázek"</string> <string name="selectable_video" msgid="1271768647699300826">"Vybratelné video"</string> <string name="selectable_item" msgid="7557320816744205280">"Vybratelná položka"</string> + <string name="role_description_button" msgid="4537198530568333649">"Tlačítko"</string> </resources> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 3a3e2062..e9d952fe 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Billede, der kan vælges"</string> <string name="selectable_video" msgid="1271768647699300826">"Video, der kan vælges"</string> <string name="selectable_item" msgid="7557320816744205280">"Element, der kan vælges"</string> + <string name="role_description_button" msgid="4537198530568333649">"Knap"</string> </resources> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 3a561101..911dd273 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Auswählbares Bild"</string> <string name="selectable_video" msgid="1271768647699300826">"Auswählbares Video"</string> <string name="selectable_item" msgid="7557320816744205280">"Auswählbares Element"</string> + <string name="role_description_button" msgid="4537198530568333649">"Schaltfläche"</string> </resources> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 8903eec1..319a3e2c 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Εικόνα με δυνατότητα επιλογής"</string> <string name="selectable_video" msgid="1271768647699300826">"Βίντεο με δυνατότητα επιλογής"</string> <string name="selectable_item" msgid="7557320816744205280">"Στοιχείο με δυνατότητα επιλογής"</string> + <string name="role_description_button" msgid="4537198530568333649">"Κουμπί"</string> </resources> diff --git a/java/res/values-en-rAU/strings.xml b/java/res/values-en-rAU/strings.xml index 53e64659..4d16a6f4 100644 --- a/java/res/values-en-rAU/strings.xml +++ b/java/res/values-en-rAU/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Selectable image"</string> <string name="selectable_video" msgid="1271768647699300826">"Selectable video"</string> <string name="selectable_item" msgid="7557320816744205280">"Selectable item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Button"</string> </resources> diff --git a/java/res/values-en-rCA/strings.xml b/java/res/values-en-rCA/strings.xml index 1c44b945..9f6d20c3 100644 --- a/java/res/values-en-rCA/strings.xml +++ b/java/res/values-en-rCA/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Selectable image"</string> <string name="selectable_video" msgid="1271768647699300826">"Selectable video"</string> <string name="selectable_item" msgid="7557320816744205280">"Selectable item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Button"</string> </resources> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 53e64659..4d16a6f4 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Selectable image"</string> <string name="selectable_video" msgid="1271768647699300826">"Selectable video"</string> <string name="selectable_item" msgid="7557320816744205280">"Selectable item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Button"</string> </resources> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index 53e64659..4d16a6f4 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Selectable image"</string> <string name="selectable_video" msgid="1271768647699300826">"Selectable video"</string> <string name="selectable_item" msgid="7557320816744205280">"Selectable item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Button"</string> </resources> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index f3b7fe85..923e9d36 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagen seleccionable"</string> <string name="selectable_video" msgid="1271768647699300826">"Video seleccionable"</string> <string name="selectable_item" msgid="7557320816744205280">"Elemento seleccionable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botón"</string> </resources> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 460de896..f7c3c0b0 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagen seleccionable"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo seleccionable"</string> <string name="selectable_item" msgid="7557320816744205280">"Elemento seleccionable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botón"</string> </resources> diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml index 85fca08f..6a17f5b3 100644 --- a/java/res/values-et/strings.xml +++ b/java/res/values-et/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Valitav pilt"</string> <string name="selectable_video" msgid="1271768647699300826">"Valitav video"</string> <string name="selectable_item" msgid="7557320816744205280">"Valitav üksus"</string> + <string name="role_description_button" msgid="4537198530568333649">"Nupp"</string> </resources> diff --git a/java/res/values-eu/strings.xml b/java/res/values-eu/strings.xml index 5020f62d..e80edad4 100644 --- a/java/res/values-eu/strings.xml +++ b/java/res/values-eu/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Hauta daitekeen irudia"</string> <string name="selectable_video" msgid="1271768647699300826">"Hauta daitekeen bideoa"</string> <string name="selectable_item" msgid="7557320816744205280">"Hauta daitekeen elementua"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botoia"</string> </resources> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 7b3dc6ea..71386d35 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -74,7 +74,7 @@ <string name="image_preview_a11y_description" msgid="297102643932491797">"ریزعکس پیشنمای تصویر"</string> <string name="video_preview_a11y_description" msgid="683440858811095990">"ریزعکس پیشنمای ویدیو"</string> <string name="file_preview_a11y_description" msgid="7397224827802410602">"ریزعکس پیشنمای فایل"</string> - <string name="chooser_no_direct_share_targets" msgid="4233416657754261844">"هیچ فردی که با او همرسانی کنید توصیه نشده است"</string> + <string name="chooser_no_direct_share_targets" msgid="4233416657754261844">"هیچ فرد توصیهشدهای برای همرسانی وجود ندارد"</string> <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> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"تصویر قابلانتخاب"</string> <string name="selectable_video" msgid="1271768647699300826">"ویدیو قابلانتخاب"</string> <string name="selectable_item" msgid="7557320816744205280">"مورد قابلانتخاب"</string> + <string name="role_description_button" msgid="4537198530568333649">"دکمه"</string> </resources> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 65244293..6938d4fa 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Valittava kuva"</string> <string name="selectable_video" msgid="1271768647699300826">"Valittava video"</string> <string name="selectable_item" msgid="7557320816744205280">"Valittava kohde"</string> + <string name="role_description_button" msgid="4537198530568333649">"Painike"</string> </resources> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index b2ae5f5c..7fdda598 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Image sélectionnable"</string> <string name="selectable_video" msgid="1271768647699300826">"Vidéo sélectionnable"</string> <string name="selectable_item" msgid="7557320816744205280">"Élément sélectionnable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Bouton"</string> </resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 2b96c92f..39d436a7 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Image sélectionnable"</string> <string name="selectable_video" msgid="1271768647699300826">"Vidéo sélectionnable"</string> <string name="selectable_item" msgid="7557320816744205280">"Élément sélectionnable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Bouton"</string> </resources> diff --git a/java/res/values-gl/strings.xml b/java/res/values-gl/strings.xml index a8caf6f3..d45e982e 100644 --- a/java/res/values-gl/strings.xml +++ b/java/res/values-gl/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imaxe seleccionable"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo seleccionable"</string> <string name="selectable_item" msgid="7557320816744205280">"Elemento seleccionable"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botón"</string> </resources> diff --git a/java/res/values-gu/strings.xml b/java/res/values-gu/strings.xml index a70a1b0f..d0e65a18 100644 --- a/java/res/values-gu/strings.xml +++ b/java/res/values-gu/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"પસંદ કરી શકાય તેવી છબી"</string> <string name="selectable_video" msgid="1271768647699300826">"પસંદ કરી શકાય તેવો વીડિયો"</string> <string name="selectable_item" msgid="7557320816744205280">"પસંદ કરી શકાય તેવી આઇટમ"</string> + <string name="role_description_button" msgid="4537198530568333649">"બટન"</string> </resources> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 3f6db1be..70da0c22 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ऐसी इमेज जिसे चुना जा सकता है"</string> <string name="selectable_video" msgid="1271768647699300826">"ऐसा वीडियो जिसे चुना जा सकता है"</string> <string name="selectable_item" msgid="7557320816744205280">"ऐसा आइटम जिसे चुना जा सकता है"</string> + <string name="role_description_button" msgid="4537198530568333649">"बटन"</string> </resources> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index 85858303..c8f8c90d 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{+ # datoteka}one{+ # datoteka}few{+ # datoteke}other{+ # datoteka}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{i još # datoteka}one{i još # datoteka}few{i još # datoteke}other{i još # datoteka}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Dijeli se tekst"</string> - <string name="sharing_link" msgid="2307694372813942916">"Dijeli se veza"</string> + <string name="sharing_link" msgid="2307694372813942916">"Veza za dijeljenje"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Podijelite sliku}one{Podijelite # sliku}few{Podijelite # slike}other{Podijelite # slika}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Dijeli se videozapis}one{Dijeli se # videozapis}few{Dijele se # videozapisa}other{Dijeli se # videozapisa}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Dijeli se # datoteka}one{Dijeli se # datoteka}few{Dijele se # datoteke}other{Dijeli se # datoteka}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Slika koja se može odabrati"</string> <string name="selectable_video" msgid="1271768647699300826">"Videozapis koji se može odabrati"</string> <string name="selectable_item" msgid="7557320816744205280">"Stavka koja se može odabrati"</string> + <string name="role_description_button" msgid="4537198530568333649">"Gumb"</string> </resources> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 792b07e2..a9e5e820 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Kijelölhető kép"</string> <string name="selectable_video" msgid="1271768647699300826">"Kijelölhető videó"</string> <string name="selectable_item" msgid="7557320816744205280">"Kijelölhető elem"</string> + <string name="role_description_button" msgid="4537198530568333649">"Gomb"</string> </resources> diff --git a/java/res/values-hy/strings.xml b/java/res/values-hy/strings.xml index f9232a5a..b0b0b235 100644 --- a/java/res/values-hy/strings.xml +++ b/java/res/values-hy/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Ընտրելու հնարավորությամբ պատկեր"</string> <string name="selectable_video" msgid="1271768647699300826">"Ընտրելու հնարավորությամբ տեսանյութ"</string> <string name="selectable_item" msgid="7557320816744205280">"Ընտրելու հնարավորությամբ տարր"</string> + <string name="role_description_button" msgid="4537198530568333649">"Կոճակ"</string> </resources> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index df05fdd0..86828b7c 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Gambar yang dapat dipilih"</string> <string name="selectable_video" msgid="1271768647699300826">"Video yang dapat dipilih"</string> <string name="selectable_item" msgid="7557320816744205280">"Item yang dapat dipilih"</string> + <string name="role_description_button" msgid="4537198530568333649">"Tombol"</string> </resources> diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml index 680ed17a..9125bae9 100644 --- a/java/res/values-is/strings.xml +++ b/java/res/values-is/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Mynd sem hægt er að velja"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeó sem hægt er að velja"</string> <string name="selectable_item" msgid="7557320816744205280">"Atriði sem hægt er að velja"</string> + <string name="role_description_button" msgid="4537198530568333649">"Hnappur"</string> </resources> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 3762f58b..7d0a7fa7 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Immagine selezionabile"</string> <string name="selectable_video" msgid="1271768647699300826">"Video selezionabile"</string> <string name="selectable_item" msgid="7557320816744205280">"Elemento selezionabile"</string> + <string name="role_description_button" msgid="4537198530568333649">"Pulsante"</string> </resources> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index bed01ff0..3c1be527 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"תמונה שניתן לבחור"</string> <string name="selectable_video" msgid="1271768647699300826">"סרטון שניתן לבחור"</string> <string name="selectable_item" msgid="7557320816744205280">"פריט שניתן לבחור"</string> + <string name="role_description_button" msgid="4537198530568333649">"לחצן"</string> </resources> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 1d2a2f06..094106c3 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"選択可能な画像"</string> <string name="selectable_video" msgid="1271768647699300826">"選択可能な動画"</string> <string name="selectable_item" msgid="7557320816744205280">"選択可能なアイテム"</string> + <string name="role_description_button" msgid="4537198530568333649">"ボタン"</string> </resources> diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml index 4675734b..e0951e39 100644 --- a/java/res/values-ka/strings.xml +++ b/java/res/values-ka/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"არჩევადი სურათი"</string> <string name="selectable_video" msgid="1271768647699300826">"არჩევადი ვიდეო"</string> <string name="selectable_item" msgid="7557320816744205280">"არჩევადი ერთეული"</string> + <string name="role_description_button" msgid="4537198530568333649">"ღილაკი"</string> </resources> diff --git a/java/res/values-kk/strings.xml b/java/res/values-kk/strings.xml index 362db640..99357ef6 100644 --- a/java/res/values-kk/strings.xml +++ b/java/res/values-kk/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Таңдауға болатын сурет"</string> <string name="selectable_video" msgid="1271768647699300826">"Таңдауға болатын бейне"</string> <string name="selectable_item" msgid="7557320816744205280">"Таңдауға болатын элемент"</string> + <string name="role_description_button" msgid="4537198530568333649">"Түйме"</string> </resources> diff --git a/java/res/values-km/strings.xml b/java/res/values-km/strings.xml index cee11e26..29d80e96 100644 --- a/java/res/values-km/strings.xml +++ b/java/res/values-km/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"រូបភាពដែលអាចជ្រើសរើសបាន"</string> <string name="selectable_video" msgid="1271768647699300826">"វីដេអូដែលអាចជ្រើសរើសបាន"</string> <string name="selectable_item" msgid="7557320816744205280">"ធាតុដែលអាចជ្រើសរើសបាន"</string> + <string name="role_description_button" msgid="4537198530568333649">"ប៊ូតុង"</string> </resources> diff --git a/java/res/values-kn/strings.xml b/java/res/values-kn/strings.xml index 35bf148c..d777b6fa 100644 --- a/java/res/values-kn/strings.xml +++ b/java/res/values-kn/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{+ # ಫೈಲ್}one{+ # ಫೈಲ್ಗಳು}other{+ # ಫೈಲ್ಗಳು}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{+ # ಇನ್ನಷ್ಟು ಫೈಲ್}one{+ # ಇನ್ನಷ್ಟು ಫೈಲ್ಗಳು}other{+ # ಇನ್ನಷ್ಟು ಫೈಲ್ಗಳು}}"</string> <string name="sharing_text" msgid="8137537443603304062">"ಪಠ್ಯ ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> - <string name="sharing_link" msgid="2307694372813942916">"ಲಿಂಕ್ ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> + <string name="sharing_link" msgid="2307694372813942916">"ಹಂಚಿಕೊಳ್ಳಬಹುದಾದ ಲಿಂಕ್"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{ಚಿತ್ರವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}one{# ಚಿತ್ರಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}other{# ಚಿತ್ರಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{ವೀಡಿಯೊವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}one{# ವೀಡಿಯೊಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}other{# ವೀಡಿಯೊಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{# ಫೈಲ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}one{# ಫೈಲ್ಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}other{# ಫೈಲ್ಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ಆಯ್ಕೆಮಾಡಬಹುದಾದ ಚಿತ್ರ"</string> <string name="selectable_video" msgid="1271768647699300826">"ಆಯ್ಕೆ ಮಾಡಬಹುದಾದ ವೀಡಿಯೊ"</string> <string name="selectable_item" msgid="7557320816744205280">"ಆಯ್ಕೆ ಮಾಡಬಹುದಾದ ಐಟಂ"</string> + <string name="role_description_button" msgid="4537198530568333649">"ಬಟನ್"</string> </resources> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 094f09b0..0ab0cefb 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"선택 가능한 이미지"</string> <string name="selectable_video" msgid="1271768647699300826">"선택 가능한 동영상"</string> <string name="selectable_item" msgid="7557320816744205280">"선택 가능한 항목"</string> + <string name="role_description_button" msgid="4537198530568333649">"버튼"</string> </resources> diff --git a/java/res/values-ky/strings.xml b/java/res/values-ky/strings.xml index 610adaf2..7de1593d 100644 --- a/java/res/values-ky/strings.xml +++ b/java/res/values-ky/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Тандала турган сүрөт"</string> <string name="selectable_video" msgid="1271768647699300826">"Тандала турган видео"</string> <string name="selectable_item" msgid="7557320816744205280">"Тандала турган нерсе"</string> + <string name="role_description_button" msgid="4537198530568333649">"Баскыч"</string> </resources> diff --git a/java/res/values-lo/strings.xml b/java/res/values-lo/strings.xml index 2cdea91f..9481a9ae 100644 --- a/java/res/values-lo/strings.xml +++ b/java/res/values-lo/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ຮູບທີ່ເລືອກໄດ້"</string> <string name="selectable_video" msgid="1271768647699300826">"ວິດີໂອທີ່ເລືອກໄດ້"</string> <string name="selectable_item" msgid="7557320816744205280">"ລາຍການທີ່ເລືອກໄດ້"</string> + <string name="role_description_button" msgid="4537198530568333649">"ປຸ່ມ"</string> </resources> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 7b0c6695..f1a0494d 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Pasirenkamas vaizdas"</string> <string name="selectable_video" msgid="1271768647699300826">"Pasirenkamas vaizdo įrašas"</string> <string name="selectable_item" msgid="7557320816744205280">"Pasirenkamas elementas"</string> + <string name="role_description_button" msgid="4537198530568333649">"Mygtukas"</string> </resources> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 1c14c2b8..5fed4d43 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{un vēl # fails}zero{un vēl # faili}one{un vēl # fails}other{un vēl # faili}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{Un vēl # fails}zero{Un vēl # failu}one{Un vēl # fails}other{Un vēl # faili}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Tiek kopīgots teksts"</string> - <string name="sharing_link" msgid="2307694372813942916">"Tiek kopīgota saite"</string> + <string name="sharing_link" msgid="2307694372813942916">"Kopīgošanas saite"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Tiek kopīgots attēls}zero{Tiek kopīgoti # attēli}one{Tiek kopīgots # attēls}other{Tiek kopīgoti # attēli}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Tiek kopīgots video}zero{Tiek kopīgoti # video}one{Tiek kopīgots # video}other{Tiek kopīgoti # video}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Notiek # faila kopīgošana}zero{Notiek # failu kopīgošana}one{Notiek # faila kopīgošana}other{Notiek # failu kopīgošana}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Atlasāms attēls"</string> <string name="selectable_video" msgid="1271768647699300826">"Atlasāms video"</string> <string name="selectable_item" msgid="7557320816744205280">"Atlasāms vienums"</string> + <string name="role_description_button" msgid="4537198530568333649">"Poga"</string> </resources> diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml index 19ff3c67..2ab3c072 100644 --- a/java/res/values-mk/strings.xml +++ b/java/res/values-mk/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Слика што може да се избере"</string> <string name="selectable_video" msgid="1271768647699300826">"Видео што може да се избере"</string> <string name="selectable_item" msgid="7557320816744205280">"Ставка што може да се избере"</string> + <string name="role_description_button" msgid="4537198530568333649">"Копче"</string> </resources> diff --git a/java/res/values-ml/strings.xml b/java/res/values-ml/strings.xml index bcd07dd7..6318a101 100644 --- a/java/res/values-ml/strings.xml +++ b/java/res/values-ml/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"തിരഞ്ഞെടുക്കാവുന്ന ചിത്രം"</string> <string name="selectable_video" msgid="1271768647699300826">"തിരഞ്ഞെടുക്കാവുന്ന വീഡിയോ"</string> <string name="selectable_item" msgid="7557320816744205280">"തിരഞ്ഞെടുക്കാവുന്ന ഇനം"</string> + <string name="role_description_button" msgid="4537198530568333649">"ബട്ടൺ"</string> </resources> diff --git a/java/res/values-mn/strings.xml b/java/res/values-mn/strings.xml index 81d97d99..8dc3cd58 100644 --- a/java/res/values-mn/strings.xml +++ b/java/res/values-mn/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Сонгох боломжтой зураг"</string> <string name="selectable_video" msgid="1271768647699300826">"Сонгох боломжтой видео"</string> <string name="selectable_item" msgid="7557320816744205280">"Сонгох боломжтой зүйл"</string> + <string name="role_description_button" msgid="4537198530568333649">"Товч"</string> </resources> diff --git a/java/res/values-mr/strings.xml b/java/res/values-mr/strings.xml index 4a061601..5e54a61a 100644 --- a/java/res/values-mr/strings.xml +++ b/java/res/values-mr/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"निवडण्यायोग्य इमेज"</string> <string name="selectable_video" msgid="1271768647699300826">"निवडण्यायोग्य व्हिडिओ"</string> <string name="selectable_item" msgid="7557320816744205280">"निवडण्यायोग्य आयटम"</string> + <string name="role_description_button" msgid="4537198530568333649">"बटण"</string> </resources> diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index a01376c6..b6dca50f 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imej yang boleh dipilih"</string> <string name="selectable_video" msgid="1271768647699300826">"Video yang boleh dipilih"</string> <string name="selectable_item" msgid="7557320816744205280">"Item yang boleh dipilih"</string> + <string name="role_description_button" msgid="4537198530568333649">"Butang"</string> </resources> diff --git a/java/res/values-my/strings.xml b/java/res/values-my/strings.xml index 9eeda078..af596656 100644 --- a/java/res/values-my/strings.xml +++ b/java/res/values-my/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ရွေးချယ်နိုင်သောပုံ"</string> <string name="selectable_video" msgid="1271768647699300826">"ရွေးချယ်နိုင်သော ဗီဒီယို"</string> <string name="selectable_item" msgid="7557320816744205280">"ရွေးချယ်နိုင်သောအရာ"</string> + <string name="role_description_button" msgid="4537198530568333649">"ခလုတ်"</string> </resources> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 7a67bc34..bd31a926 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Bilde som kan velges"</string> <string name="selectable_video" msgid="1271768647699300826">"Video som kan velges"</string> <string name="selectable_item" msgid="7557320816744205280">"Element som kan velges"</string> + <string name="role_description_button" msgid="4537198530568333649">"Knapp"</string> </resources> diff --git a/java/res/values-ne/strings.xml b/java/res/values-ne/strings.xml index 76365455..112329c6 100644 --- a/java/res/values-ne/strings.xml +++ b/java/res/values-ne/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"चयन गर्न मिल्ने फोटो"</string> <string name="selectable_video" msgid="1271768647699300826">"चयन गर्न मिल्ने भिडियो"</string> <string name="selectable_item" msgid="7557320816744205280">"चयन गर्न मिल्ने वस्तु"</string> + <string name="role_description_button" msgid="4537198530568333649">"बटन"</string> </resources> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index e452e98e..54123bef 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Selecteerbare afbeelding"</string> <string name="selectable_video" msgid="1271768647699300826">"Selecteerbare video"</string> <string name="selectable_item" msgid="7557320816744205280">"Selecteerbaar item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Knop"</string> </resources> diff --git a/java/res/values-or/strings.xml b/java/res/values-or/strings.xml index 0e2ece56..785acbe1 100644 --- a/java/res/values-or/strings.xml +++ b/java/res/values-or/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ଚୟନ କରାଯାଇପାରୁଥିବା ଇମେଜ"</string> <string name="selectable_video" msgid="1271768647699300826">"ଚୟନ କରାଯାଇପାରୁଥିବା ଭିଡିଓ"</string> <string name="selectable_item" msgid="7557320816744205280">"ଚୟନ କରାଯାଇପାରୁଥିବା ଆଇଟମ"</string> + <string name="role_description_button" msgid="4537198530568333649">"ବଟନ"</string> </resources> diff --git a/java/res/values-pa/strings.xml b/java/res/values-pa/strings.xml index 607f7d26..8b9f528c 100644 --- a/java/res/values-pa/strings.xml +++ b/java/res/values-pa/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ਚੁਣਨਯੋਗ ਚਿੱਤਰ"</string> <string name="selectable_video" msgid="1271768647699300826">"ਚੁਣਨਯੋਗ ਵੀਡੀਓ"</string> <string name="selectable_item" msgid="7557320816744205280">"ਚੁਣਨਯੋਗ ਆਈਟਮ"</string> + <string name="role_description_button" msgid="4537198530568333649">"ਬਟਨ"</string> </resources> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 10dda621..3de2b1f4 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Obraz do wyboru"</string> <string name="selectable_video" msgid="1271768647699300826">"Film do wyboru"</string> <string name="selectable_item" msgid="7557320816744205280">"Element do wyboru"</string> + <string name="role_description_button" msgid="4537198530568333649">"Przycisk"</string> </resources> diff --git a/java/res/values-pt-rBR/strings.xml b/java/res/values-pt-rBR/strings.xml index c8ce55a8..5ed57493 100644 --- a/java/res/values-pt-rBR/strings.xml +++ b/java/res/values-pt-rBR/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{Mais # arquivo}one{Mais # arquivo}many{Mais # de arquivos}other{Mais # arquivos}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{Mais # arquivo}one{Mais # arquivo}many{Mais # de arquivos}other{Mais # arquivos}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Compartilhando texto"</string> - <string name="sharing_link" msgid="2307694372813942916">"Compartilhando link"</string> + <string name="sharing_link" msgid="2307694372813942916">"Compartilhar link"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Compartilhar imagem}one{Compartilhar # imagem}many{Compartilhar # de imagens}other{Compartilhar # imagens}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Compartilhando vídeo}one{Compartilhando # vídeo}many{Compartilhando # de vídeos}other{Compartilhando # vídeos}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Compartilhando # arquivo}one{Compartilhando # arquivo}many{Compartilhando # de arquivos}other{Compartilhando # arquivos}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagem selecionável"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo selecionável"</string> <string name="selectable_item" msgid="7557320816744205280">"Item selecionável"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botão"</string> </resources> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index ffcf9a1e..73d12957 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagem selecionável"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo selecionável"</string> <string name="selectable_item" msgid="7557320816744205280">"Item selecionável"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botão"</string> </resources> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index c8ce55a8..5ed57493 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{Mais # arquivo}one{Mais # arquivo}many{Mais # de arquivos}other{Mais # arquivos}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{Mais # arquivo}one{Mais # arquivo}many{Mais # de arquivos}other{Mais # arquivos}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Compartilhando texto"</string> - <string name="sharing_link" msgid="2307694372813942916">"Compartilhando link"</string> + <string name="sharing_link" msgid="2307694372813942916">"Compartilhar link"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Compartilhar imagem}one{Compartilhar # imagem}many{Compartilhar # de imagens}other{Compartilhar # imagens}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Compartilhando vídeo}one{Compartilhando # vídeo}many{Compartilhando # de vídeos}other{Compartilhando # vídeos}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Compartilhando # arquivo}one{Compartilhando # arquivo}many{Compartilhando # de arquivos}other{Compartilhando # arquivos}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagem selecionável"</string> <string name="selectable_video" msgid="1271768647699300826">"Vídeo selecionável"</string> <string name="selectable_item" msgid="7557320816744205280">"Item selecionável"</string> + <string name="role_description_button" msgid="4537198530568333649">"Botão"</string> </resources> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index c2843bab..7c8816b6 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -74,7 +74,7 @@ <string name="image_preview_a11y_description" msgid="297102643932491797">"Miniatură pentru previzualizarea imaginii"</string> <string name="video_preview_a11y_description" msgid="683440858811095990">"Miniatură pentru previzualizarea videoclipului"</string> <string name="file_preview_a11y_description" msgid="7397224827802410602">"Miniatură pentru previzualizarea fișierului"</string> - <string name="chooser_no_direct_share_targets" msgid="4233416657754261844">"Nu există persoane recomandate pentru permiterea accesului"</string> + <string name="chooser_no_direct_share_targets" msgid="4233416657754261844">"Nu există persoane recomandate pentru trimitere"</string> <string name="usb_device_resolve_prompt_warn" msgid="4254493957548169620">"Permisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string> <string name="resolver_personal_tab" msgid="1381052735324320565">"Personal"</string> <string name="resolver_work_tab" msgid="3588325717455216412">"Serviciu"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imagine care poate fi selectată"</string> <string name="selectable_video" msgid="1271768647699300826">"Videoclip care poate fi selectat"</string> <string name="selectable_item" msgid="7557320816744205280">"Articol care poate fi selectat"</string> + <string name="role_description_button" msgid="4537198530568333649">"Buton"</string> </resources> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 9b4c2d20..e0934ad1 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Изображение, которое можно выбрать"</string> <string name="selectable_video" msgid="1271768647699300826">"Видео, которое можно выбрать"</string> <string name="selectable_item" msgid="7557320816744205280">"Объект, который можно выбрать"</string> + <string name="role_description_button" msgid="4537198530568333649">"Кнопка"</string> </resources> diff --git a/java/res/values-si/strings.xml b/java/res/values-si/strings.xml index 1fc87e4d..19af7794 100644 --- a/java/res/values-si/strings.xml +++ b/java/res/values-si/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"තෝරා ගත හැකි රූපය"</string> <string name="selectable_video" msgid="1271768647699300826">"තෝරා ගත හැකි වීඩියෝව"</string> <string name="selectable_item" msgid="7557320816744205280">"තෝරා ගත හැකි අයිතමය"</string> + <string name="role_description_button" msgid="4537198530568333649">"බොත්තම"</string> </resources> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 9119aaa0..36898690 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Vybrateľný obrázok"</string> <string name="selectable_video" msgid="1271768647699300826">"Vybrateľné video"</string> <string name="selectable_item" msgid="7557320816744205280">"Vybrateľná položka"</string> + <string name="role_description_button" msgid="4537198530568333649">"Tlačidlo"</string> </resources> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 78e07ad1..714ba171 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Slika, ki jo je mogoče izbrati."</string> <string name="selectable_video" msgid="1271768647699300826">"Videoposnetek, ki ga je mogoče izbrati."</string> <string name="selectable_item" msgid="7557320816744205280">"Element, ki ga je mogoče izbrati."</string> + <string name="role_description_button" msgid="4537198530568333649">"Gumb"</string> </resources> diff --git a/java/res/values-sq/strings.xml b/java/res/values-sq/strings.xml index 374b2e0a..dba01219 100644 --- a/java/res/values-sq/strings.xml +++ b/java/res/values-sq/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Imazh që mund të zgjidhet"</string> <string name="selectable_video" msgid="1271768647699300826">"Video që mund të zgjidhet"</string> <string name="selectable_item" msgid="7557320816744205280">"Artikull që mund të zgjidhet"</string> + <string name="role_description_button" msgid="4537198530568333649">"Buton"</string> </resources> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 8e7c57d1..8591ef7d 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Слика која може да се изабере"</string> <string name="selectable_video" msgid="1271768647699300826">"Видео који може да се изабере"</string> <string name="selectable_item" msgid="7557320816744205280">"Ставка која може да се изабере"</string> + <string name="role_description_button" msgid="4537198530568333649">"Дугме"</string> </resources> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index d48cc781..6810faa7 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Bild som kan markeras"</string> <string name="selectable_video" msgid="1271768647699300826">"Video som kan markeras"</string> <string name="selectable_item" msgid="7557320816744205280">"Objekt som kan markeras"</string> + <string name="role_description_button" msgid="4537198530568333649">"Knapp"</string> </resources> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 2f63e887..56496041 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -56,8 +56,8 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{+ faili #}other{+ faili #}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{Faili nyingine #}other{Faili zingine #}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Kutuma maandishi"</string> - <string name="sharing_link" msgid="2307694372813942916">"Inashiriki kiungo"</string> - <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Inashiriki picha}other{Inashiriki picha #}}"</string> + <string name="sharing_link" msgid="2307694372813942916">"Inatuma kiungo"</string> + <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Kutuma picha}other{Kutuma picha #}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Inashiriki video}other{Inashiriki video #}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Inashiriki faili #}other{Inashiriki faili #}}"</string> <string name="select_items_to_share" msgid="1026071777275022579">"Chagua vipengee vya kutuma"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Picha inayoweza kuchaguliwa"</string> <string name="selectable_video" msgid="1271768647699300826">"Video inayoweza kuchaguliwa"</string> <string name="selectable_item" msgid="7557320816744205280">"Kipengee kinachoweza kuchaguliwa"</string> + <string name="role_description_button" msgid="4537198530568333649">"Kitufe"</string> </resources> diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml index 240ee067..e152ba06 100644 --- a/java/res/values-sw600dp/dimens.xml +++ b/java/res/values-sw600dp/dimens.xml @@ -20,4 +20,5 @@ <resources> <dimen name="chooser_width">624dp</dimen> <dimen name="modify_share_text_toggle_max_width">250dp</dimen> + <dimen name="chooser_item_focus_outline_corner_radius">16dp</dimen> </resources> diff --git a/java/res/values-ta/strings.xml b/java/res/values-ta/strings.xml index f1df5cba..f53e5b29 100644 --- a/java/res/values-ta/strings.xml +++ b/java/res/values-ta/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"தேர்ந்தெடுக்கக்கூடிய படம்"</string> <string name="selectable_video" msgid="1271768647699300826">"தேர்ந்தெடுக்கக்கூடிய வீடியோ"</string> <string name="selectable_item" msgid="7557320816744205280">"தேர்ந்தெடுக்கக்கூடியது"</string> + <string name="role_description_button" msgid="4537198530568333649">"பட்டன்"</string> </resources> diff --git a/java/res/values-te/strings.xml b/java/res/values-te/strings.xml index b88d7d4e..5003d8eb 100644 --- a/java/res/values-te/strings.xml +++ b/java/res/values-te/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"ఎంచుకోదగిన ఇమేజ్"</string> <string name="selectable_video" msgid="1271768647699300826">"ఎంచుకోదగిన వీడియో"</string> <string name="selectable_item" msgid="7557320816744205280">"ఎంచుకోదగిన ఐటెమ్"</string> + <string name="role_description_button" msgid="4537198530568333649">"బటన్"</string> </resources> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 5effd16c..8bb9408f 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"รูปภาพที่เลือกได้"</string> <string name="selectable_video" msgid="1271768647699300826">"วิดีโอที่เลือกได้"</string> <string name="selectable_item" msgid="7557320816744205280">"รายการที่เลือกได้"</string> + <string name="role_description_button" msgid="4537198530568333649">"ปุ่ม"</string> </resources> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index 67782253..e98c06bf 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Napipiling larawan"</string> <string name="selectable_video" msgid="1271768647699300826">"Napipiling video"</string> <string name="selectable_item" msgid="7557320816744205280">"Napipiling item"</string> + <string name="role_description_button" msgid="4537198530568333649">"Button"</string> </resources> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 5dee9296..25b7e860 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -56,7 +56,7 @@ <string name="other_files" msgid="4501185823517473875">"{count,plural, =1{+ # dosya}other{+ # dosya}}"</string> <string name="more_files" msgid="1043875756612339842">"{count,plural, =1{+ # dosya daha}other{+ # dosya daha}}"</string> <string name="sharing_text" msgid="8137537443603304062">"Metin paylaşılıyor"</string> - <string name="sharing_link" msgid="2307694372813942916">"Bağlantı paylaşılıyor"</string> + <string name="sharing_link" msgid="2307694372813942916">"Paylaşım bağlantısı"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Resim paylaşılıyor}other{# resim paylaşılıyor}}"</string> <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Video paylaşılıyor}other{# video paylaşılıyor}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{# dosya paylaşılıyor}other{# dosya paylaşılıyor}}"</string> @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Seçilebilir resim"</string> <string name="selectable_video" msgid="1271768647699300826">"Seçilebilir video"</string> <string name="selectable_item" msgid="7557320816744205280">"Seçilebilir öğe"</string> + <string name="role_description_button" msgid="4537198530568333649">"Düğme"</string> </resources> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 293696fd..33f9e350 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Зображення, яке можна вибрати"</string> <string name="selectable_video" msgid="1271768647699300826">"Відео, яке можна вибрати"</string> <string name="selectable_item" msgid="7557320816744205280">"Об’єкт, який можна вибрати"</string> + <string name="role_description_button" msgid="4537198530568333649">"Кнопка"</string> </resources> diff --git a/java/res/values-ur/strings.xml b/java/res/values-ur/strings.xml index 9ecc8443..950041e7 100644 --- a/java/res/values-ur/strings.xml +++ b/java/res/values-ur/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"قابل انتخاب تصویر"</string> <string name="selectable_video" msgid="1271768647699300826">"قابل انتخاب ویڈیو"</string> <string name="selectable_item" msgid="7557320816744205280">"قابل انتخاب آئٹم"</string> + <string name="role_description_button" msgid="4537198530568333649">"بٹن"</string> </resources> diff --git a/java/res/values-uz/strings.xml b/java/res/values-uz/strings.xml index f9434b18..1792e0d2 100644 --- a/java/res/values-uz/strings.xml +++ b/java/res/values-uz/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Tanlanadigan rasm"</string> <string name="selectable_video" msgid="1271768647699300826">"Tanlanadigan video"</string> <string name="selectable_item" msgid="7557320816744205280">"Tanlanadigan fayl"</string> + <string name="role_description_button" msgid="4537198530568333649">"Tugma"</string> </resources> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 4c84256e..a32bacc1 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Hình ảnh có thể chọn"</string> <string name="selectable_video" msgid="1271768647699300826">"Video có thể chọn"</string> <string name="selectable_item" msgid="7557320816744205280">"Mục có thể chọn"</string> + <string name="role_description_button" msgid="4537198530568333649">"Nút"</string> </resources> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index c2fa444f..603a4e5e 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"可选择的图片"</string> <string name="selectable_video" msgid="1271768647699300826">"可选择的视频"</string> <string name="selectable_item" msgid="7557320816744205280">"可选择的内容"</string> + <string name="role_description_button" msgid="4537198530568333649">"按钮"</string> </resources> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index 54a61c7e..b3aed885 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"可以揀嘅圖片"</string> <string name="selectable_video" msgid="1271768647699300826">"可以揀嘅影片"</string> <string name="selectable_item" msgid="7557320816744205280">"可以揀嘅項目"</string> + <string name="role_description_button" msgid="4537198530568333649">"按鈕"</string> </resources> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 0d369318..97770baf 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"可選取的圖片"</string> <string name="selectable_video" msgid="1271768647699300826">"可選取的影片"</string> <string name="selectable_item" msgid="7557320816744205280">"可選取的項目"</string> + <string name="role_description_button" msgid="4537198530568333649">"按鈕"</string> </resources> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 9d6d13dc..bdf42d69 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -106,4 +106,5 @@ <string name="selectable_image" msgid="3157858923437182271">"Umfanekiso okhethekayo"</string> <string name="selectable_video" msgid="1271768647699300826">"Ividiyo ekhethekayo"</string> <string name="selectable_item" msgid="7557320816744205280">"Into ekhethekayo"</string> + <string name="role_description_button" msgid="4537198530568333649">"Inkinobho"</string> </resources> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index c9f2c300..19d85573 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -55,5 +55,13 @@ <attr name="itemInnerSpacing" format="dimension" /> <attr name="itemOuterSpacing" format="dimension" /> <attr name="maxWidthHint" format="dimension" /> + <attr name="editButtonRoleDescription" format="string" /> + </declare-styleable> + + <declare-styleable name="ChooserTargetItemView"> + <attr name="focusOutlineColor" format="color" /> + <attr name="focusInnerOutlineColor" format="color" /> + <attr name="focusOutlineWidth" format="dimension" /> + <attr name="focusOutlineCornerRadius" format="dimension" /> </declare-styleable> </resources> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index a1f03276..515343b6 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -34,9 +34,15 @@ <dimen name="chooser_max_collapsed_height">288dp</dimen> <dimen name="chooser_icon_size">56dp</dimen> <dimen name="chooser_badge_size">22dp</dimen> + <dimen name="chooser_icon_horizontal_padding">8dp</dimen> + <dimen name="chooser_icon_vertical_padding">7dp</dimen> + <dimen name="chooser_icon_width_with_padding">72dp</dimen> <!-- = chooser_icon_size + chooser_icon_horizontal_padding * 2 --> + <dimen name="chooser_icon_height_with_padding">70dp</dimen> <!-- = chooser_icon_size + chooser_icon_vertical_padding * 2 --> <dimen name="chooser_headline_text_size">18sp</dimen> <dimen name="chooser_grid_target_name_text_size">12sp</dimen> <dimen name="chooser_grid_activity_name_text_size">12sp</dimen> + <dimen name="chooser_item_focus_outline_corner_radius">11dp</dimen> + <dimen name="chooser_item_focus_outline_width">2dp</dimen> <dimen name="resolver_icon_size">32dp</dimen> <dimen name="resolver_button_bar_spacing">0dp</dimen> <dimen name="resolver_badge_size">18dp</dimen> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 4f77d248..2261a4a8 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -338,4 +338,6 @@ <!-- Accessibility content description for an item that the user may select for sharing. [CHAR LIMIT=NONE] --> <string name="selectable_item">Selectable item</string> + <!-- Accessibility role description for a11y on button. [CHAR LIMIT=NONE] --> + <string name="role_description_button">Button</string> </resources> diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index c8387c4e..54f575d7 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -25,6 +25,7 @@ import static androidx.lifecycle.LifecycleKt.getCoroutineScope; import static com.android.intentresolver.ChooserActionFactory.EDIT_SOURCE; import static com.android.intentresolver.Flags.fixShortcutsFlashing; import static com.android.intentresolver.Flags.keyboardNavigationFix; +import static com.android.intentresolver.Flags.rebuildAdaptersOnTargetPinning; import static com.android.intentresolver.Flags.shareouselUpdateExcludeComponentsExtra; import static com.android.intentresolver.Flags.unselectFinalItem; import static com.android.intentresolver.ext.CreationExtrasExtKt.replaceDefaultArgs; @@ -171,7 +172,6 @@ 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 - @@ -257,16 +257,13 @@ public class ChooserActivity extends Hilt_ChooserActivity implements @Inject @Background public CoroutineDispatcher mBackgroundDispatcher; @Inject public ChooserHelper mChooserHelper; @Inject public FeatureFlags mFeatureFlags; - @Inject public android.service.chooser.FeatureFlags mChooserServiceFeatureFlags; @Inject public EventLog mEventLog; @Inject @AppPredictionAvailable public boolean mAppPredictionAvailable; @Inject @ImageEditor public Optional<ComponentName> mImageEditor; @Inject @NearbyShare public Optional<ComponentName> mNearbyShare; - protected TargetDataLoader mTargetDataLoader; - @Inject public Provider<TargetDataLoader> mTargetDataLoaderProvider; @Inject @Caching - public Provider<TargetDataLoader> mCachingTargetDataLoaderProvider; + public TargetDataLoader mTargetDataLoader; @Inject public DevicePolicyResources mDevicePolicyResources; @Inject public ProfilePagerResources mProfilePagerResources; @Inject public PackageManager mPackageManager; @@ -345,20 +342,14 @@ public class ChooserActivity extends Hilt_ChooserActivity implements Log.i(TAG, "onCreate"); mActivityModelRepository.initialize(this::createActivityModel); - mTargetDataLoader = mChooserServiceFeatureFlags.chooserPayloadToggling() - ? mCachingTargetDataLoaderProvider.get() - : mTargetDataLoaderProvider.get(); - setTheme(R.style.Theme_DeviceDefault_Chooser); // Initializer is invoked when this function returns, via Lifecycle. mChooserHelper.setInitializer(this::initialize); - if (mChooserServiceFeatureFlags.chooserPayloadToggling()) { - mChooserHelper.setOnChooserRequestChanged(this::onChooserRequestChanged); - mChooserHelper.setOnPendingSelection(this::onPendingSelection); - if (unselectFinalItem()) { - mChooserHelper.setOnHasSelections(this::onHasSelections); - } + mChooserHelper.setOnChooserRequestChanged(this::onChooserRequestChanged); + mChooserHelper.setOnPendingSelection(this::onPendingSelection); + if (unselectFinalItem()) { + mChooserHelper.setOnHasSelections(this::onHasSelections); } } private int mInitialProfile = -1; @@ -659,8 +650,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements mEnterTransitionAnimationDelegate, new HeadlineGeneratorImpl(this), mRequest.getContentTypeHint(), - mRequest.getMetadataText(), - mChooserServiceFeatureFlags.chooserPayloadToggling()); + mRequest.getMetadataText()); updateStickyContentPreview(); if (shouldShowStickyContentPreview()) { getEventLog().logActionShareWithPreview( @@ -777,9 +767,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements } private void recreatePagerAdapter() { - if (!mChooserServiceFeatureFlags.chooserPayloadToggling()) { - return; - } destroyProfileRecords(); createProfileRecords( new AppPredictorFactory( @@ -852,6 +839,9 @@ public class ChooserActivity extends Hilt_ChooserActivity implements } } setTabsViewEnabled(false); + if (mSystemWindowInsets != null) { + applyFooterView(mSystemWindowInsets.bottom); + } } private void setTabsViewEnabled(boolean isEnabled) { @@ -1559,10 +1549,14 @@ public class ChooserActivity extends Hilt_ChooserActivity implements private void handlePackagesChanged(@Nullable ResolverListAdapter listAdapter) { // Refresh pinned items mPinnedSharedPrefs = getPinnedSharedPrefs(this); - if (listAdapter == null) { - mChooserMultiProfilePagerAdapter.refreshPackagesInAllTabs(); + if (rebuildAdaptersOnTargetPinning()) { + recreatePagerAdapter(); } else { - listAdapter.handlePackagesChanged(); + if (listAdapter == null) { + mChooserMultiProfilePagerAdapter.refreshPackagesInAllTabs(); + } else { + listAdapter.handlePackagesChanged(); + } } } @@ -2454,17 +2448,12 @@ public class ChooserActivity extends Hilt_ChooserActivity implements // ResolverListAdapter#mPostListReadyRunnable is executed. if (chooserListAdapter.getDisplayResolveInfoCount() == 0) { Log.d(TAG, "getDisplayResolveInfoCount() == 0"); - if (rebuildComplete && mChooserServiceFeatureFlags.chooserPayloadToggling()) { + if (rebuildComplete) { onAppTargetsLoaded(listAdapter); } chooserListAdapter.notifyDataSetChanged(); } else { - if (mChooserServiceFeatureFlags.chooserPayloadToggling()) { - chooserListAdapter.updateAlphabeticalList( - () -> onAppTargetsLoaded(listAdapter)); - } else { - chooserListAdapter.updateAlphabeticalList(); - } + chooserListAdapter.updateAlphabeticalList(() -> onAppTargetsLoaded(listAdapter)); } if (rebuildComplete) { diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index 016eb714..563d7d1a 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -18,6 +18,7 @@ package com.android.intentresolver; import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; +import static com.android.intentresolver.Flags.targetHoverAndKeyboardFocusStates; import android.app.ActivityManager; import android.app.prediction.AppTarget; @@ -59,6 +60,8 @@ import com.android.intentresolver.widget.BadgeTextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; +import com.google.common.collect.ImmutableList; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -365,7 +368,10 @@ public class ChooserListAdapter extends ResolverListAdapter { @Override View onCreateView(ViewGroup parent) { - return mInflater.inflate(R.layout.chooser_grid_item, parent, false); + int layout = targetHoverAndKeyboardFocusStates() + ? R.layout.chooser_grid_item_hover + : R.layout.chooser_grid_item; + return mInflater.inflate(layout, parent, false); } @Override @@ -512,18 +518,13 @@ public class ChooserListAdapter extends ResolverListAdapter { /** * Group application targets */ - public void updateAlphabeticalList() { - updateAlphabeticalList(() -> {}); - } - - /** - * Group application targets - */ public void updateAlphabeticalList(Runnable onCompleted) { final DisplayResolveInfoAzInfoComparator comparator = new DisplayResolveInfoAzInfoComparator(mContext); - final List<DisplayResolveInfo> allTargets = new ArrayList<>(); - allTargets.addAll(getTargetsInCurrentDisplayList()); + ImmutableList<DisplayResolveInfo> displayList = getTargetsInCurrentDisplayList(); + final List<DisplayResolveInfo> allTargets = + new ArrayList<>(displayList.size() + mCallerTargets.size()); + allTargets.addAll(displayList); allTargets.addAll(mCallerTargets); new AsyncTask<Void, Void, List<DisplayResolveInfo>>() { @@ -543,6 +544,24 @@ public class ChooserListAdapter extends ResolverListAdapter { // Consolidate multiple targets from same app. return allTargets .stream() + .map(appTarget -> { + if (targetHoverAndKeyboardFocusStates()) { + // Icon drawables are effectively cached per target info. + // Without cloning target infos, the same target info could be used + // for two different positions in the grid: once in the ranked + // targets row (from ResolverListAdapter#mDisplayList or + // #mCallerTargets, see #getItem()) and again in the all-app-target + // grid (copied from #mDisplayList and #mCallerTargets to + // #mSortedList). + // Using the same drawable for two list items would result in visual + // effects being applied to both simultaneously. + DisplayResolveInfo copy = appTarget.copy(); + copy.getDisplayIconHolder().setDisplayIcon(null); + return copy; + } else { + return appTarget; + } + }) .collect(Collectors.groupingBy(target -> target.getResolvedComponentName().getPackageName() + "#" + target.getDisplayLabel() diff --git a/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java b/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java index ae80fad4..8070fc84 100644 --- a/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java +++ b/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -136,7 +137,7 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment final TargetPresentationGetter pg = getProvidingAppPresentationGetter(); title.setText(isShortcutTarget() ? mShortcutTitle : pg.getLabel()); - icon.setImageDrawable(pg.getIcon(mUserHandle)); + icon.setImageDrawable(new BitmapDrawable(getResources(), pg.getIconBitmap(mUserHandle))); rv.setAdapter(new VHAdapter(items)); return v; @@ -280,7 +281,11 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment final int iconDpi = am.getLauncherLargeIconDensity(); // Use the matching application icon and label for the title, any TargetInfo will do - return new TargetPresentationGetter.Factory(getContext(), iconDpi) + final Context context = getContext(); + return new TargetPresentationGetter.Factory( + () -> SimpleIconFactory.obtain(context), + context.getPackageManager(), + iconDpi) .makePresentationGetter(mTargetInfos.get(0).getResolveInfo()); } diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index 2f220cf1..38259281 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -150,6 +150,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements @Inject public IntentForwarding mIntentForwarding; @Inject public FeatureFlags mFeatureFlags; @Inject public ActivityModelRepository mActivityModelRepository; + @Inject public DefaultTargetDataLoader.Factory mTargetDataLoaderFactory; private ResolverViewModel mViewModel; private ResolverRequest mRequest; @@ -334,10 +335,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements mProfileAvailability.setOnProfileStatusChange(this::onWorkProfileStatusUpdated); mResolvingHome = mRequest.isResolvingHome(); - mTargetDataLoader = new DefaultTargetDataLoader( - this, - getLifecycle(), - mRequest.isAudioCaptureDevice()); + mTargetDataLoader = mTargetDataLoaderFactory.create(mRequest.isAudioCaptureDevice()); // The last argument of createResolverListAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always diff --git a/java/src/com/android/intentresolver/ShortcutSelectionLogic.java b/java/src/com/android/intentresolver/ShortcutSelectionLogic.java index 2d5ec451..3a1a51e3 100644 --- a/java/src/com/android/intentresolver/ShortcutSelectionLogic.java +++ b/java/src/com/android/intentresolver/ShortcutSelectionLogic.java @@ -16,6 +16,8 @@ package com.android.intentresolver; +import static com.android.intentresolver.Flags.rebuildAdaptersOnTargetPinning; + import android.app.prediction.AppTarget; import android.content.Context; import android.content.Intent; @@ -171,16 +173,21 @@ public class ShortcutSelectionLogic { List<TargetInfo> serviceTargets) { // Check for duplicates and abort if found - for (TargetInfo otherTargetInfo : serviceTargets) { + for (int i = 0; i < serviceTargets.size(); i++) { + TargetInfo otherTargetInfo = serviceTargets.get(i); if (chooserTargetInfo.isSimilar(otherTargetInfo)) { + if (rebuildAdaptersOnTargetPinning() + && chooserTargetInfo.isPinned() != otherTargetInfo.isPinned()) { + serviceTargets.set(i, chooserTargetInfo); + return true; + } return false; } } int currentSize = serviceTargets.size(); final float newScore = chooserTargetInfo.getModifiedScore(); - for (int i = 0; i < Math.min(currentSize, maxRankedTargets); - i++) { + for (int i = 0; i < Math.min(currentSize, maxRankedTargets); i++) { final TargetInfo serviceTarget = serviceTargets.get(i); if (serviceTarget == null) { serviceTargets.set(i, chooserTargetInfo); diff --git a/java/src/com/android/intentresolver/SimpleIconFactory.java b/java/src/com/android/intentresolver/SimpleIconFactory.java index f4871e36..afb7d19e 100644 --- a/java/src/com/android/intentresolver/SimpleIconFactory.java +++ b/java/src/com/android/intentresolver/SimpleIconFactory.java @@ -64,7 +64,7 @@ import java.util.Optional; * possibly badged. It is intended to be used only by Sharesheet for the Q release with custom code. */ @Deprecated -public class SimpleIconFactory { +public class SimpleIconFactory implements AutoCloseable { private static final SynchronizedPool<SimpleIconFactory> sPool = @@ -139,6 +139,11 @@ public class SimpleIconFactory { "Expected theme to define iconfactoryBadgeSize."); } + @Override + public void close() { + recycle(); + } + /** * Recycles the SimpleIconFactory so others may use it. * @@ -146,9 +151,11 @@ public class SimpleIconFactory { */ @Deprecated public void recycle() { - // Return to default background color - setWrapperBackgroundColor(Color.WHITE); - sPool.release(this); + if (sPoolEnabled) { + // Return to default background color + setWrapperBackgroundColor(Color.WHITE); + sPool.release(this); + } } /** diff --git a/java/src/com/android/intentresolver/TargetPresentationGetter.java b/java/src/com/android/intentresolver/TargetPresentationGetter.java index 910c65c9..3a7f807d 100644 --- a/java/src/com/android/intentresolver/TargetPresentationGetter.java +++ b/java/src/com/android/intentresolver/TargetPresentationGetter.java @@ -16,14 +16,12 @@ package com.android.intentresolver; -import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.text.TextUtils; @@ -31,6 +29,8 @@ import android.util.Log; import androidx.annotation.Nullable; +import javax.inject.Provider; + /** * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application icon * and label over any IntentFilter or Activity icon to increase user understanding, with an @@ -49,22 +49,29 @@ public abstract class TargetPresentationGetter { /** Helper to build appropriate type-specific {@link TargetPresentationGetter} instances. */ public static class Factory { - private final Context mContext; + private final Provider<SimpleIconFactory> mIconFactoryProvider; + private final PackageManager mPackageManager; private final int mIconDpi; - public Factory(Context context, int iconDpi) { - mContext = context; + public Factory( + Provider<SimpleIconFactory> iconfactoryProvider, + PackageManager packageManager, + int iconDpi) { + mIconFactoryProvider = iconfactoryProvider; + mPackageManager = packageManager; mIconDpi = iconDpi; } /** Make a {@link TargetPresentationGetter} for an {@link ActivityInfo}. */ public TargetPresentationGetter makePresentationGetter(ActivityInfo activityInfo) { - return new ActivityInfoPresentationGetter(mContext, mIconDpi, activityInfo); + return new ActivityInfoPresentationGetter( + mIconFactoryProvider, mPackageManager, mIconDpi, activityInfo); } /** Make a {@link TargetPresentationGetter} for a {@link ResolveInfo}. */ public TargetPresentationGetter makePresentationGetter(ResolveInfo resolveInfo) { - return new ResolveInfoPresentationGetter(mContext, mIconDpi, resolveInfo); + return new ResolveInfoPresentationGetter( + mIconFactoryProvider, mPackageManager, mIconDpi, resolveInfo); } } @@ -77,7 +84,7 @@ public abstract class TargetPresentationGetter { @Nullable protected abstract String getAppLabelForSubstitutePermission(); - private Context mContext; + private final Provider<SimpleIconFactory> mIconFactoryProvider; private final int mIconDpi; private final boolean mHasSubstitutePermission; private final ApplicationInfo mAppInfo; @@ -88,14 +95,6 @@ public abstract class TargetPresentationGetter { * Retrieve the image that should be displayed as the icon when this target is presented to the * specified {@code userHandle}. */ - public Drawable getIcon(UserHandle userHandle) { - return new BitmapDrawable(mContext.getResources(), getIconBitmap(userHandle)); - } - - /** - * Retrieve the image that should be displayed as the icon when this target is presented to the - * specified {@code userHandle}. - */ public Bitmap getIconBitmap(@Nullable UserHandle userHandle) { Drawable drawable = null; if (mHasSubstitutePermission) { @@ -116,9 +115,10 @@ public abstract class TargetPresentationGetter { drawable = mAppInfo.loadIcon(mPm); } - SimpleIconFactory iconFactory = SimpleIconFactory.obtain(mContext); - Bitmap icon = iconFactory.createUserBadgedIconBitmap(drawable, userHandle); - iconFactory.recycle(); + Bitmap icon; + try (SimpleIconFactory iconFactory = mIconFactoryProvider.get()) { + icon = iconFactory.createUserBadgedIconBitmap(drawable, userHandle); + } return icon; } @@ -168,9 +168,13 @@ public abstract class TargetPresentationGetter { return res.getDrawableForDensity(resId, mIconDpi); } - private TargetPresentationGetter(Context context, int iconDpi, ApplicationInfo appInfo) { - mContext = context; - mPm = context.getPackageManager(); + private TargetPresentationGetter( + Provider<SimpleIconFactory> iconfactoryProvider, + PackageManager packageManager, + int iconDpi, + ApplicationInfo appInfo) { + mIconFactoryProvider = iconfactoryProvider; + mPm = packageManager; mAppInfo = appInfo; mIconDpi = iconDpi; mHasSubstitutePermission = (PackageManager.PERMISSION_GRANTED == mPm.checkPermission( @@ -183,8 +187,11 @@ public abstract class TargetPresentationGetter { private final ResolveInfo mResolveInfo; ResolveInfoPresentationGetter( - Context context, int iconDpi, ResolveInfo resolveInfo) { - super(context, iconDpi, resolveInfo.activityInfo); + Provider<SimpleIconFactory> iconfactoryProvider, + PackageManager packageManager, + int iconDpi, + ResolveInfo resolveInfo) { + super(iconfactoryProvider, packageManager, iconDpi, resolveInfo.activityInfo); mResolveInfo = resolveInfo; } @@ -230,8 +237,11 @@ public abstract class TargetPresentationGetter { private final ActivityInfo mActivityInfo; ActivityInfoPresentationGetter( - Context context, int iconDpi, ActivityInfo activityInfo) { - super(context, iconDpi, activityInfo.applicationInfo); + Provider<SimpleIconFactory> iconfactoryProvider, + PackageManager packageManager, + int iconDpi, + ActivityInfo activityInfo) { + super(iconfactoryProvider, packageManager, iconDpi, activityInfo.applicationInfo); mActivityInfo = activityInfo; } diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index 5e44c53e..f0674a27 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -239,4 +239,11 @@ public class DisplayResolveInfo implements TargetInfo { public void setPinned(boolean pinned) { mPinned = pinned; } + + /** + * Creates a copy of the object. + */ + public DisplayResolveInfo copy() { + return new DisplayResolveInfo(this); + } } diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java index ba6c3c05..e5f40001 100644 --- a/java/src/com/android/intentresolver/chooser/TargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java @@ -65,7 +65,7 @@ public interface TargetInfo { * @param icon the icon to return on subsequent calls to {@link #getDisplayIcon()}. * Implementations may discard this request as a no-op if they don't support setting. */ - void setDisplayIcon(Drawable icon); + void setDisplayIcon(@Nullable Drawable icon); } /** A simple mutable-container implementation of {@link IconHolder}. */ @@ -78,7 +78,7 @@ public interface TargetInfo { return mDisplayIcon; } - public void setDisplayIcon(Drawable icon) { + public void setDisplayIcon(@Nullable Drawable icon) { mDisplayIcon = icon; } } diff --git a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java index 1128ec5d..4166e5ae 100644 --- a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java +++ b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java @@ -51,7 +51,6 @@ import java.util.function.Supplier; public final class ChooserContentPreviewUi { private final CoroutineScope mScope; - private final boolean mIsPayloadTogglingEnabled; /** * Delegate to build the default system action buttons to display in the preview layout, if/when @@ -109,11 +108,8 @@ public final class ChooserContentPreviewUi { TransitionElementStatusCallback transitionElementStatusCallback, HeadlineGenerator headlineGenerator, ContentTypeHint contentTypeHint, - @Nullable CharSequence metadata, - // TODO: replace with the FeatureFlag ref when v1 is gone - boolean isPayloadTogglingEnabled) { + @Nullable CharSequence metadata) { mScope = scope; - mIsPayloadTogglingEnabled = isPayloadTogglingEnabled; mModifyShareActionFactory = modifyShareActionFactory; mContentPreviewUi = createContentPreview( previewData, @@ -169,7 +165,7 @@ public final class ChooserContentPreviewUi { return fileContentPreviewUi; } - if (previewType == CONTENT_PREVIEW_PAYLOAD_SELECTION && mIsPayloadTogglingEnabled) { + if (previewType == CONTENT_PREVIEW_PAYLOAD_SELECTION) { transitionElementStatusCallback.onAllTransitionElementsReady(); // TODO return new ShareouselContentPreviewUi(); } diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt index 07cbaa04..d7b9077d 100644 --- a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt +++ b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt @@ -24,7 +24,6 @@ import android.provider.DocumentsContract import android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL import android.provider.Downloads import android.provider.OpenableColumns -import android.service.chooser.Flags.chooserPayloadToggling import android.text.TextUtils import android.util.Log import androidx.annotation.OpenForTesting @@ -133,7 +132,7 @@ constructor( * IMAGE, FILE, TEXT. */ if (!targetIntent.isSend || records.isEmpty()) { CONTENT_PREVIEW_TEXT - } else if (chooserPayloadToggling() && shouldShowPayloadSelection()) { + } else if (shouldShowPayloadSelection()) { // TODO: replace with the proper flags injection CONTENT_PREVIEW_PAYLOAD_SELECTION } else { diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt index 4fe5e8d5..fc193eca 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt @@ -17,14 +17,13 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import android.content.Intent -import com.android.intentresolver.Flags.shareouselUpdateExcludeComponentsExtra import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.CustomAction import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.PendingIntentSender import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.toCustomActionModel import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.getOrDefault import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.onValue import com.android.intentresolver.data.repository.ChooserRequestRepository +import com.android.intentresolver.domain.updateWith import javax.inject.Inject import kotlinx.coroutines.flow.update @@ -36,28 +35,7 @@ constructor( @CustomAction private val pendingIntentSender: PendingIntentSender, ) { fun applyUpdate(targetIntent: Intent, update: ShareouselUpdate) { - repository.chooserRequest.update { current -> - current.copy( - targetIntent = targetIntent, - callerChooserTargets = - update.callerTargets.getOrDefault(current.callerChooserTargets), - modifyShareAction = - update.modifyShareAction.getOrDefault(current.modifyShareAction), - additionalTargets = update.alternateIntents.getOrDefault(current.additionalTargets), - chosenComponentSender = - update.resultIntentSender.getOrDefault(current.chosenComponentSender), - refinementIntentSender = - update.refinementIntentSender.getOrDefault(current.refinementIntentSender), - metadataText = update.metadataText.getOrDefault(current.metadataText), - chooserActions = update.customActions.getOrDefault(current.chooserActions), - filteredComponentNames = - if (shareouselUpdateExcludeComponentsExtra()) { - update.excludeComponents.getOrDefault(current.filteredComponentNames) - } else { - current.filteredComponentNames - } - ) - } + repository.chooserRequest.update { it.updateWith(targetIntent, update) } update.customActions.onValue { actions -> repository.customActions.value = actions.map { it.toCustomActionModel(pendingIntentSender) } diff --git a/java/src/com/android/intentresolver/domain/ChooserRequestExt.kt b/java/src/com/android/intentresolver/domain/ChooserRequestExt.kt new file mode 100644 index 00000000..5ca3ad20 --- /dev/null +++ b/java/src/com/android/intentresolver/domain/ChooserRequestExt.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.domain + +import android.content.Intent +import android.content.Intent.EXTRA_ALTERNATE_INTENTS +import android.content.Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS +import android.content.Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION +import android.content.Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER +import android.content.Intent.EXTRA_CHOOSER_RESULT_INTENT_SENDER +import android.content.Intent.EXTRA_CHOOSER_TARGETS +import android.content.Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER +import android.content.Intent.EXTRA_EXCLUDE_COMPONENTS +import android.content.Intent.EXTRA_INTENT +import android.content.Intent.EXTRA_METADATA_TEXT +import android.os.Bundle +import com.android.intentresolver.Flags.shareouselUpdateExcludeComponentsExtra +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.getOrDefault +import com.android.intentresolver.data.model.ChooserRequest + +/** Creates a new ChooserRequest with the target intent and updates from a Shareousel callback */ +fun ChooserRequest.updateWith(targetIntent: Intent, update: ShareouselUpdate): ChooserRequest = + copy( + targetIntent = targetIntent, + callerChooserTargets = update.callerTargets.getOrDefault(callerChooserTargets), + modifyShareAction = update.modifyShareAction.getOrDefault(modifyShareAction), + additionalTargets = update.alternateIntents.getOrDefault(additionalTargets), + chosenComponentSender = update.resultIntentSender.getOrDefault(chosenComponentSender), + refinementIntentSender = update.refinementIntentSender.getOrDefault(refinementIntentSender), + metadataText = update.metadataText.getOrDefault(metadataText), + chooserActions = update.customActions.getOrDefault(chooserActions), + filteredComponentNames = + if (shareouselUpdateExcludeComponentsExtra()) { + update.excludeComponents.getOrDefault(filteredComponentNames) + } else { + filteredComponentNames + }, + ) + +/** Save ChooserRequest values that can be updated by the Shareousel into a Bundle */ +fun ChooserRequest.saveUpdates(bundle: Bundle): Bundle { + bundle.putParcelable(EXTRA_INTENT, targetIntent) + bundle.putParcelableArray(EXTRA_CHOOSER_TARGETS, callerChooserTargets.toTypedArray()) + bundle.putParcelable(EXTRA_CHOOSER_MODIFY_SHARE_ACTION, modifyShareAction) + bundle.putParcelableArray(EXTRA_ALTERNATE_INTENTS, additionalTargets.toTypedArray()) + bundle.putParcelable(EXTRA_CHOOSER_RESULT_INTENT_SENDER, chosenComponentSender) + bundle.putParcelable(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, chosenComponentSender) + bundle.putParcelable(EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER, refinementIntentSender) + bundle.putCharSequence(EXTRA_METADATA_TEXT, metadataText) + bundle.putParcelableArray(EXTRA_CHOOSER_CUSTOM_ACTIONS, chooserActions.toTypedArray()) + if (shareouselUpdateExcludeComponentsExtra()) { + bundle.putParcelableArray(EXTRA_EXCLUDE_COMPONENTS, filteredComponentNames.toTypedArray()) + } + return bundle +} diff --git a/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java b/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java index 2eceb89c..f09fcfc5 100644 --- a/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java +++ b/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java @@ -17,34 +17,31 @@ package com.android.intentresolver.icons; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; import android.os.AsyncTask; -import com.android.intentresolver.R; +import androidx.annotation.Nullable; + import com.android.intentresolver.TargetPresentationGetter; import java.util.function.Consumer; -abstract class BaseLoadIconTask extends AsyncTask<Void, Void, Drawable> { +abstract class BaseLoadIconTask extends AsyncTask<Void, Void, Bitmap> { protected final Context mContext; protected final TargetPresentationGetter.Factory mPresentationFactory; - private final Consumer<Drawable> mCallback; + private final Consumer<Bitmap> mCallback; BaseLoadIconTask( Context context, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Consumer<Bitmap> callback) { mContext = context; mPresentationFactory = presentationFactory; mCallback = callback; } - protected final Drawable loadIconPlaceholder() { - return mContext.getDrawable(R.drawable.resolver_icon_placeholder); - } - @Override - protected final void onPostExecute(Drawable d) { + protected final void onPostExecute(@Nullable Bitmap d) { mCallback.accept(d); } } diff --git a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt index 8474b4c3..793b7621 100644 --- a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt @@ -17,9 +17,13 @@ package com.android.intentresolver.icons import android.content.ComponentName +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.UserHandle import androidx.collection.LruCache +import com.android.intentresolver.Flags.targetHoverAndKeyboardFocusStates import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.chooser.SelectableTargetInfo import java.util.function.Consumer @@ -28,23 +32,24 @@ import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.BINARY) annotation class Caching -private typealias IconCache = LruCache<String, Drawable> +private typealias IconCache = LruCache<String, Bitmap> class CachingTargetDataLoader( + private val context: Context, private val targetDataLoader: TargetDataLoader, private val cacheSize: Int = 100, -) : TargetDataLoader() { +) : TargetDataLoader { @GuardedBy("self") private val perProfileIconCache = HashMap<UserHandle, IconCache>() override fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, - callback: Consumer<Drawable> + callback: Consumer<Drawable>, ): Drawable? { val cacheKey = info.toCacheKey() - return getCachedAppIcon(cacheKey, userHandle) + return getCachedAppIcon(cacheKey, userHandle)?.toDrawable() ?: targetDataLoader.getOrLoadAppTargetIcon(info, userHandle) { drawable -> - getProfileIconCache(userHandle).put(cacheKey, drawable) + drawable.extractBitmap()?.let { getProfileIconCache(userHandle).put(cacheKey, it) } callback.accept(drawable) } } @@ -52,13 +57,15 @@ class CachingTargetDataLoader( override fun getOrLoadDirectShareIcon( info: SelectableTargetInfo, userHandle: UserHandle, - callback: Consumer<Drawable> + callback: Consumer<Drawable>, ): Drawable? { val cacheKey = info.toCacheKey() - return cacheKey?.let { getCachedAppIcon(it, userHandle) } + return cacheKey?.let { getCachedAppIcon(it, userHandle) }?.toDrawable() ?: targetDataLoader.getOrLoadDirectShareIcon(info, userHandle) { drawable -> if (cacheKey != null) { - getProfileIconCache(userHandle).put(cacheKey, drawable) + drawable.extractBitmap()?.let { + getProfileIconCache(userHandle).put(cacheKey, it) + } } callback.accept(drawable) } @@ -69,7 +76,7 @@ class CachingTargetDataLoader( override fun getOrLoadLabel(info: DisplayResolveInfo) = targetDataLoader.getOrLoadLabel(info) - private fun getCachedAppIcon(component: String, userHandle: UserHandle): Drawable? = + private fun getCachedAppIcon(component: String, userHandle: UserHandle): Bitmap? = getProfileIconCache(userHandle)[component] private fun getProfileIconCache(userHandle: UserHandle): IconCache = @@ -78,10 +85,7 @@ class CachingTargetDataLoader( } private fun DisplayResolveInfo.toCacheKey() = - ComponentName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name, - ) + ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) .flattenToString() private fun SelectableTargetInfo.toCacheKey(): String? = @@ -95,4 +99,20 @@ class CachingTargetDataLoader( append(directShareShortcutInfo?.id ?: "") } } + + private fun Bitmap.toDrawable(): Drawable { + return if (targetHoverAndKeyboardFocusStates()) { + HoverBitmapDrawable(this) + } else { + BitmapDrawable(context.resources, this) + } + } + + private fun Drawable.extractBitmap(): Bitmap? { + return when (this) { + is BitmapDrawable -> bitmap + is HoverBitmapDrawable -> bitmap + else -> null + } + } } diff --git a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt index e7392f58..1ff1ddfa 100644 --- a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt @@ -16,8 +16,9 @@ package com.android.intentresolver.icons -import android.app.ActivityManager import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.AsyncTask import android.os.UserHandle @@ -26,27 +27,34 @@ import androidx.annotation.GuardedBy import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner +import com.android.intentresolver.Flags.targetHoverAndKeyboardFocusStates +import com.android.intentresolver.R +import com.android.intentresolver.SimpleIconFactory import com.android.intentresolver.TargetPresentationGetter import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.chooser.SelectableTargetInfo +import com.android.intentresolver.inject.ActivityOwned +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ActivityContext import java.util.concurrent.atomic.AtomicInteger import java.util.function.Consumer +import javax.inject.Provider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor /** An actual [TargetDataLoader] implementation. */ // TODO: replace async tasks with coroutines. -class DefaultTargetDataLoader( - private val context: Context, - private val lifecycle: Lifecycle, - private val isAudioCaptureDevice: Boolean, -) : TargetDataLoader() { - private val presentationFactory = - TargetPresentationGetter.Factory( - context, - context.getSystemService(ActivityManager::class.java)?.launcherLargeIconDensity - ?: error("Unable to access ActivityManager") - ) +class DefaultTargetDataLoader +@AssistedInject +constructor( + @ActivityContext private val context: Context, + @ActivityOwned private val lifecycle: Lifecycle, + private val iconFactoryProvider: Provider<SimpleIconFactory>, + private val presentationFactory: TargetPresentationGetter.Factory, + @Assisted private val isAudioCaptureDevice: Boolean, +) : TargetDataLoader { private val nextTaskId = AtomicInteger(0) @GuardedBy("self") private val activeTasks = SparseArray<AsyncTask<*, *, *>>() private val executor = Dispatchers.IO.asExecutor() @@ -68,9 +76,9 @@ class DefaultTargetDataLoader( callback: Consumer<Drawable>, ): Drawable? { val taskId = nextTaskId.getAndIncrement() - LoadIconTask(context, info, userHandle, presentationFactory) { result -> + LoadIconTask(context, info, presentationFactory) { bitmap -> removeTask(taskId) - callback.accept(result) + callback.accept(bitmap?.toDrawable() ?: loadIconPlaceholder()) } .also { addTask(taskId, it) } .executeOnExecutor(executor) @@ -87,9 +95,10 @@ class DefaultTargetDataLoader( context.createContextAsUser(userHandle, 0), info, presentationFactory, - ) { result -> + iconFactoryProvider, + ) { bitmap -> removeTask(taskId) - callback.accept(result) + callback.accept(bitmap?.toDrawable() ?: loadIconPlaceholder()) } .also { addTask(taskId, it) } .executeOnExecutor(executor) @@ -123,6 +132,9 @@ class DefaultTargetDataLoader( synchronized(activeTasks) { activeTasks.remove(id) } } + private fun loadIconPlaceholder(): Drawable = + requireNotNull(context.getDrawable(R.drawable.resolver_icon_placeholder)) + private fun destroy() { synchronized(activeTasks) { for (i in 0 until activeTasks.size()) { @@ -131,4 +143,17 @@ class DefaultTargetDataLoader( activeTasks.clear() } } + + private fun Bitmap.toDrawable(): Drawable { + return if (targetHoverAndKeyboardFocusStates()) { + HoverBitmapDrawable(this) + } else { + BitmapDrawable(context.resources, this) + } + } + + @AssistedFactory + interface Factory { + fun create(isAudioCaptureDevice: Boolean): DefaultTargetDataLoader + } } diff --git a/java/src/com/android/intentresolver/icons/HoverBitmapDrawable.kt b/java/src/com/android/intentresolver/icons/HoverBitmapDrawable.kt new file mode 100644 index 00000000..4a21df92 --- /dev/null +++ b/java/src/com/android/intentresolver/icons/HoverBitmapDrawable.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.icons + +import android.graphics.Bitmap +import com.android.launcher3.icons.FastBitmapDrawable + +/** A [FastBitmapDrawable] extension that provides access to the bitmap. */ +class HoverBitmapDrawable(val bitmap: Bitmap) : FastBitmapDrawable(bitmap) { + + override fun newConstantState(): FastBitmapConstantState { + return HoverBitmapDrawableState(bitmap, iconColor) + } + + private class HoverBitmapDrawableState(private val bitmap: Bitmap, color: Int) : + FastBitmapConstantState(bitmap, color) { + override fun createDrawable(): FastBitmapDrawable { + return HoverBitmapDrawable(bitmap) + } + } + + companion object { + init { + setFlagHoverEnabled(true) + } + } +} diff --git a/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java b/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java index e2c0362d..01f9330e 100644 --- a/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java +++ b/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java @@ -23,7 +23,6 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Trace; @@ -39,30 +38,36 @@ import com.android.intentresolver.util.UriFilters; import java.util.function.Consumer; +import javax.inject.Provider; + /** * Loads direct share targets icons. */ class LoadDirectShareIconTask extends BaseLoadIconTask { private static final String TAG = "DirectShareIconTask"; private final SelectableTargetInfo mTargetInfo; + private final Provider<SimpleIconFactory> mIconFactoryProvider; LoadDirectShareIconTask( Context context, SelectableTargetInfo targetInfo, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Provider<SimpleIconFactory> iconFactoryProvider, + Consumer<Bitmap> callback) { super(context, presentationFactory, callback); + mIconFactoryProvider = iconFactoryProvider; mTargetInfo = targetInfo; } @Override - protected Drawable doInBackground(Void... voids) { - Drawable drawable = null; + @Nullable + protected Bitmap doInBackground(Void... voids) { + Bitmap iconBitmap = null; Trace.beginSection("shortcut-icon"); try { final Icon icon = mTargetInfo.getChooserTargetIcon(); if (icon == null || UriFilters.hasValidIcon(icon)) { - drawable = getChooserTargetIconDrawable( + iconBitmap = getChooserTargetIconBitmap( mContext, icon, mTargetInfo.getChooserTargetComponentName(), @@ -71,25 +76,21 @@ class LoadDirectShareIconTask extends BaseLoadIconTask { Log.e(TAG, "Failed to load shortcut icon for " + mTargetInfo.getChooserTargetComponentName() + "; no access"); } - if (drawable == null) { - drawable = loadIconPlaceholder(); - } } catch (Exception e) { Log.e( TAG, "Failed to load shortcut icon for " + mTargetInfo.getChooserTargetComponentName(), e); - drawable = loadIconPlaceholder(); } finally { Trace.endSection(); } - return drawable; + return iconBitmap; } @WorkerThread @Nullable - private Drawable getChooserTargetIconDrawable( + private Bitmap getChooserTargetIconBitmap( Context context, @Nullable Icon icon, ComponentName targetComponentName, @@ -125,10 +126,11 @@ class LoadDirectShareIconTask extends BaseLoadIconTask { Bitmap appIcon = mPresentationFactory.makePresentationGetter(info).getIconBitmap(null); // Raster target drawable with appIcon as a badge - SimpleIconFactory sif = SimpleIconFactory.obtain(context); - Bitmap directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon); - sif.recycle(); + Bitmap directShareBadgedIcon; + try (SimpleIconFactory sif = mIconFactoryProvider.get()) { + directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon); + } - return new BitmapDrawable(context.getResources(), directShareBadgedIcon); + return directShareBadgedIcon; } } diff --git a/java/src/com/android/intentresolver/icons/LoadIconTask.java b/java/src/com/android/intentresolver/icons/LoadIconTask.java index 75132208..4573fadf 100644 --- a/java/src/com/android/intentresolver/icons/LoadIconTask.java +++ b/java/src/com/android/intentresolver/icons/LoadIconTask.java @@ -19,11 +19,12 @@ package com.android.intentresolver.icons; import android.content.ComponentName; import android.content.Context; import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; import android.os.Trace; -import android.os.UserHandle; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.intentresolver.TargetPresentationGetter; import com.android.intentresolver.chooser.DisplayResolveInfo; @@ -32,38 +33,36 @@ import java.util.function.Consumer; class LoadIconTask extends BaseLoadIconTask { private static final String TAG = "IconTask"; protected final DisplayResolveInfo mDisplayResolveInfo; - private final UserHandle mUserHandle; private final ResolveInfo mResolveInfo; LoadIconTask( Context context, DisplayResolveInfo dri, - UserHandle userHandle, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Consumer<Bitmap> callback) { super(context, presentationFactory, callback); - mUserHandle = userHandle; mDisplayResolveInfo = dri; mResolveInfo = dri.getResolveInfo(); } @Override - protected Drawable doInBackground(Void... params) { + @Nullable + protected Bitmap doInBackground(Void... params) { Trace.beginSection("app-icon"); try { return loadIconForResolveInfo(mResolveInfo); } catch (Exception e) { ComponentName componentName = mDisplayResolveInfo.getResolvedComponentName(); Log.e(TAG, "Failed to load app icon for " + componentName, e); - return loadIconPlaceholder(); + return null; } finally { Trace.endSection(); } } - protected final Drawable loadIconForResolveInfo(ResolveInfo ri) { + protected final Bitmap loadIconForResolveInfo(ResolveInfo ri) { // Load icons based on userHandle from ResolveInfo. If in work profile/clone profile, icons // should be badged. - return mPresentationFactory.makePresentationGetter(ri).getIcon(ri.userHandle); + return mPresentationFactory.makePresentationGetter(ri).getIconBitmap(ri.userHandle); } } diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt index 935b527a..7cbd040e 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt @@ -23,24 +23,24 @@ import com.android.intentresolver.chooser.SelectableTargetInfo import java.util.function.Consumer /** A target data loader contract. Added to support testing. */ -abstract class TargetDataLoader { +interface TargetDataLoader { /** Load an app target icon */ - abstract fun getOrLoadAppTargetIcon( + fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, callback: Consumer<Drawable>, ): Drawable? /** Load a shortcut icon */ - abstract fun getOrLoadDirectShareIcon( + fun getOrLoadDirectShareIcon( info: SelectableTargetInfo, userHandle: UserHandle, callback: Consumer<Drawable>, ): Drawable? /** Load target label */ - abstract fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) + fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) /** Loads DisplayResolveInfo's display label synchronously, if needed */ - abstract fun getOrLoadLabel(info: DisplayResolveInfo) + fun getOrLoadLabel(info: DisplayResolveInfo) } diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt index 9c0acb11..d6d4aae1 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt @@ -16,29 +16,45 @@ package com.android.intentresolver.icons +import android.app.ActivityManager import android.content.Context -import androidx.lifecycle.Lifecycle -import com.android.intentresolver.inject.ActivityOwned +import android.content.pm.PackageManager +import com.android.intentresolver.SimpleIconFactory +import com.android.intentresolver.TargetPresentationGetter import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.scopes.ActivityScoped +import javax.inject.Provider @Module @InstallIn(ActivityComponent::class) object TargetDataLoaderModule { @Provides - @ActivityScoped - fun targetDataLoader( - @ActivityContext context: Context, - @ActivityOwned lifecycle: Lifecycle, - ): TargetDataLoader = DefaultTargetDataLoader(context, lifecycle, isAudioCaptureDevice = false) + fun simpleIconFactory(@ActivityContext context: Context): SimpleIconFactory = + SimpleIconFactory.obtain(context) + + @Provides + fun presentationGetterFactory( + iconFactoryProvider: Provider<SimpleIconFactory>, + packageManager: PackageManager, + activityManager: ActivityManager, + ): TargetPresentationGetter.Factory = + TargetPresentationGetter.Factory( + iconFactoryProvider, + packageManager, + activityManager.launcherLargeIconDensity, + ) @Provides @ActivityScoped @Caching - fun cachingTargetDataLoader(targetDataLoader: TargetDataLoader): TargetDataLoader = - CachingTargetDataLoader(targetDataLoader) + fun cachingTargetDataLoader( + @ActivityContext context: Context, + dataLoaderFactory: DefaultTargetDataLoader.Factory, + ): TargetDataLoader = + // Intended to be used in Chooser only thus the hardcoded isAudioCaptureDevice value. + CachingTargetDataLoader(context, dataLoaderFactory.create(isAudioCaptureDevice = false)) } diff --git a/java/src/com/android/intentresolver/inject/ActivityModelModule.kt b/java/src/com/android/intentresolver/inject/ActivityModelModule.kt index 7201bd2b..60eff925 100644 --- a/java/src/com/android/intentresolver/inject/ActivityModelModule.kt +++ b/java/src/com/android/intentresolver/inject/ActivityModelModule.kt @@ -18,9 +18,13 @@ package com.android.intentresolver.inject import android.content.Intent import android.net.Uri +import android.os.Bundle import android.service.chooser.ChooserAction +import androidx.lifecycle.SavedStateHandle +import com.android.intentresolver.Flags.saveShareouselState import com.android.intentresolver.data.model.ChooserRequest import com.android.intentresolver.data.repository.ActivityModelRepository +import com.android.intentresolver.ui.viewmodel.CHOOSER_REQUEST_KEY import com.android.intentresolver.ui.viewmodel.readChooserRequest import com.android.intentresolver.util.ownedByCurrentUser import com.android.intentresolver.validation.Valid @@ -44,8 +48,12 @@ object ActivityModelModule { @ViewModelScoped fun provideInitialRequest( activityModelRepo: ActivityModelRepository, - flags: ChooserServiceFlags, - ): ValidationResult<ChooserRequest> = readChooserRequest(activityModelRepo.value, flags) + savedStateHandle: SavedStateHandle, + ): ValidationResult<ChooserRequest> { + val activityModel = activityModelRepo.value + val extras = restoreChooserRequestExtras(activityModel.intent.extras, savedStateHandle) + return readChooserRequest(activityModel, extras) + } @Provides fun provideChooserRequest(initialRequest: ValidationResult<ChooserRequest>): ChooserRequest = @@ -117,3 +125,18 @@ private val Intent.contentUris: Sequence<Uri> } } } + +private fun restoreChooserRequestExtras( + initialExtras: Bundle?, + savedStateHandle: SavedStateHandle, +): Bundle = + if (saveShareouselState()) { + savedStateHandle.get<Bundle>(CHOOSER_REQUEST_KEY)?.let { savedSateBundle -> + Bundle().apply { + initialExtras?.let { putAll(it) } + putAll(savedSateBundle) + } + } ?: initialExtras + } else { + initialExtras + } ?: Bundle() diff --git a/java/src/com/android/intentresolver/ui/viewmodel/ChooserRequestReader.kt b/java/src/com/android/intentresolver/ui/viewmodel/ChooserRequestReader.kt index 13cadf37..13de84b2 100644 --- a/java/src/com/android/intentresolver/ui/viewmodel/ChooserRequestReader.kt +++ b/java/src/com/android/intentresolver/ui/viewmodel/ChooserRequestReader.kt @@ -36,7 +36,6 @@ import android.content.Intent.EXTRA_TEXT import android.content.Intent.EXTRA_TITLE import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT -import android.content.IntentFilter import android.content.IntentSender import android.net.Uri import android.os.Bundle @@ -48,7 +47,6 @@ import com.android.intentresolver.R import com.android.intentresolver.data.model.ChooserRequest import com.android.intentresolver.ext.hasSendAction import com.android.intentresolver.ext.ifMatch -import com.android.intentresolver.inject.ChooserServiceFlags import com.android.intentresolver.shared.model.ActivityModel import com.android.intentresolver.util.hasValidIcon import com.android.intentresolver.validation.Validation @@ -69,11 +67,10 @@ internal fun Intent.maybeAddSendActionFlags() = fun readChooserRequest( model: ActivityModel, - flags: ChooserServiceFlags, + savedState: Bundle = model.intent.extras ?: Bundle(), ): ValidationResult<ChooserRequest> { - val extras = model.intent.extras ?: Bundle() @Suppress("DEPRECATION") - return validateFrom(extras::get) { + return validateFrom(savedState::get) { val targetIntent = required(IntentOrUri(EXTRA_INTENT)).maybeAddSendActionFlags() val isSendAction = targetIntent.hasSendAction() @@ -126,7 +123,7 @@ fun readChooserRequest( val additionalContentUri: Uri? val focusedItemPos: Int - if (isSendAction && flags.chooserPayloadToggling()) { + if (isSendAction) { additionalContentUri = optional(value<Uri>(EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI)) focusedItemPos = optional(value<Int>(EXTRA_CHOOSER_FOCUSED_ITEM_POSITION)) ?: 0 } else { @@ -166,7 +163,7 @@ fun readChooserRequest( refinementIntentSender = refinementIntentSender, sharedText = sharedText, sharedTextTitle = sharedTextTitle, - shareTargetFilter = targetIntent.toShareTargetFilter(), + shareTargetFilter = targetIntent.createIntentFilter(), additionalContentUri = additionalContentUri, focusedItemPosition = focusedItemPos, contentTypeHint = contentTypeHint, @@ -182,12 +179,3 @@ fun Validation.readChooserActions(): List<ChooserAction>? = optional(array<ChooserAction>(EXTRA_CHOOSER_CUSTOM_ACTIONS)) ?.filter { hasValidIcon(it) } ?.take(MAX_CHOOSER_ACTIONS) - -private fun Intent.toShareTargetFilter(): IntentFilter? { - return type?.let { - IntentFilter().apply { - action?.also { addAction(it) } - addDataType(it) - } - } -} diff --git a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt index fe7e9109..8597d802 100644 --- a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt +++ b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt @@ -16,9 +16,12 @@ package com.android.intentresolver.ui.viewmodel import android.content.ContentInterface +import android.os.Bundle import android.util.Log +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.android.intentresolver.Flags.saveShareouselState import com.android.intentresolver.contentpreview.ImageLoader import com.android.intentresolver.contentpreview.PreviewDataProvider import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.FetchPreviewsInteractor @@ -27,8 +30,8 @@ import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.Shar import com.android.intentresolver.data.model.ChooserRequest import com.android.intentresolver.data.repository.ActivityModelRepository import com.android.intentresolver.data.repository.ChooserRequestRepository +import com.android.intentresolver.domain.saveUpdates import com.android.intentresolver.inject.Background -import com.android.intentresolver.inject.ChooserServiceFlags import com.android.intentresolver.shared.model.ActivityModel import com.android.intentresolver.validation.Invalid import com.android.intentresolver.validation.Valid @@ -43,17 +46,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.plus private const val TAG = "ChooserViewModel" +const val CHOOSER_REQUEST_KEY = "chooser-request" @HiltViewModel class ChooserViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, activityModelRepository: ActivityModelRepository, private val shareouselViewModelProvider: Lazy<ShareouselViewModel>, private val processUpdatesInteractor: Lazy<ProcessTargetIntentUpdatesInteractor>, private val fetchPreviewsInteractor: Lazy<FetchPreviewsInteractor>, @Background private val bgDispatcher: CoroutineDispatcher, - private val flags: ChooserServiceFlags, /** * Provided only for the express purpose of early exit in the event of an invalid request. * @@ -71,10 +75,6 @@ constructor( val shareouselViewModel: ShareouselViewModel by lazy { // TODO: consolidate this logic, this would require a consolidated preview view model but // for now just postpone starting the payload selection preview machinery until it's needed - assert(flags.chooserPayloadToggling()) { - "An attempt to use payload selection preview with the disabled flag" - } - viewModelScope.launch(bgDispatcher) { processUpdatesInteractor.get().activate() } viewModelScope.launch(bgDispatcher) { fetchPreviewsInteractor.get().activate() } shareouselViewModelProvider.get() @@ -99,8 +99,24 @@ constructor( } init { - if (initialRequest is Invalid) { - Log.w(TAG, "initialRequest is Invalid, initialization failed") + when (initialRequest) { + is Invalid -> { + Log.w(TAG, "initialRequest is Invalid, initialization failed") + } + is Valid<ChooserRequest> -> { + if (saveShareouselState()) { + val isRestored = + savedStateHandle.get<Bundle>(CHOOSER_REQUEST_KEY)?.takeIf { !it.isEmpty } != + null + savedStateHandle.setSavedStateProvider(CHOOSER_REQUEST_KEY) { + Bundle().also { result -> + request.value + .takeIf { isRestored || it != initialRequest.value } + ?.saveUpdates(result) + } + } + } + } } } } diff --git a/java/src/com/android/intentresolver/ui/viewmodel/IntentExt.kt b/java/src/com/android/intentresolver/ui/viewmodel/IntentExt.kt new file mode 100644 index 00000000..30f16d20 --- /dev/null +++ b/java/src/com/android/intentresolver/ui/viewmodel/IntentExt.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.intentresolver.ui.viewmodel + +import android.content.Intent +import android.content.IntentFilter +import android.content.IntentFilter.MalformedMimeTypeException +import android.net.Uri +import android.os.PatternMatcher + +/** Collects Uris from standard locations within the Intent. */ +fun Intent.collectUris(): Set<Uri> = buildSet { + data?.also { add(it) } + @Suppress("DEPRECATION") + when (val stream = extras?.get(Intent.EXTRA_STREAM)) { + is Uri -> add(stream) + is ArrayList<*> -> addAll(stream.mapNotNull { it as? Uri }) + else -> Unit + } + clipData?.apply { (0..<itemCount).mapNotNull { getItemAt(it).uri }.forEach(::add) } +} + +fun IntentFilter.addUri(uri: Uri) { + uri.scheme?.also { addDataScheme(it) } + uri.host?.also { addDataAuthority(it, null) } + uri.path?.also { addDataPath(it, PatternMatcher.PATTERN_LITERAL) } +} + +fun Intent.createIntentFilter(): IntentFilter? { + val uris = collectUris() + if (action == null && uris.isEmpty()) { + // at least one is required to be meaningful + return null + } + return IntentFilter().also { filter -> + type?.also { + try { + filter.addDataType(it) + } catch (_: MalformedMimeTypeException) { // ignore malformed type + } + } + action?.also { filter.addAction(it) } + uris.forEach(filter::addUri) + } +} diff --git a/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt b/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt new file mode 100644 index 00000000..b5a4d617 --- /dev/null +++ b/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.widget + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.util.TypedValue +import android.view.MotionEvent +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import com.android.intentresolver.R + +class ChooserTargetItemView( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int, +) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { + private val outlineRadius: Float + private val outlineWidth: Float + private val outlinePaint: Paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE } + private val outlineInnerPaint: Paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE } + private var iconView: ImageView? = null + + constructor(context: Context) : this(context, null) + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + ) : this(context, attrs, defStyleAttr, 0) + + init { + val a = context.obtainStyledAttributes(attrs, R.styleable.ChooserTargetItemView) + val defaultWidth = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 2f, + context.resources.displayMetrics, + ) + outlineRadius = + a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineCornerRadius, 0f) + outlineWidth = + a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineWidth, defaultWidth) + + outlinePaint.strokeWidth = outlineWidth + outlinePaint.color = + a.getColor(R.styleable.ChooserTargetItemView_focusOutlineColor, Color.TRANSPARENT) + + outlineInnerPaint.strokeWidth = outlineWidth + outlineInnerPaint.color = + a.getColor(R.styleable.ChooserTargetItemView_focusInnerOutlineColor, Color.TRANSPARENT) + a.recycle() + } + + override fun onViewAdded(child: View) { + super.onViewAdded(child) + if (child is ImageView) { + iconView = child + } + } + + override fun onViewRemoved(child: View?) { + super.onViewRemoved(child) + if (child === iconView) { + iconView = null + } + } + + override fun onHoverEvent(event: MotionEvent): Boolean { + val iconView = iconView ?: return false + if (!isEnabled) return true + when (event.action) { + MotionEvent.ACTION_HOVER_ENTER -> { + iconView.isHovered = true + } + MotionEvent.ACTION_HOVER_EXIT -> { + iconView.isHovered = false + } + } + return true + } + + override fun onInterceptHoverEvent(event: MotionEvent?) = true + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + if (isFocused) { + drawFocusInnerOutline(canvas) + drawFocusOutline(canvas) + } + } + + private fun drawFocusInnerOutline(canvas: Canvas) { + val outlineOffset = outlineWidth + outlineWidth / 2 + canvas.drawRoundRect( + outlineOffset, + outlineOffset, + maxOf(0f, width - outlineOffset), + maxOf(0f, height - outlineOffset), + outlineRadius - outlineWidth, + outlineRadius - outlineWidth, + outlineInnerPaint, + ) + } + + private fun drawFocusOutline(canvas: Canvas) { + val outlineOffset = outlineWidth / 2 + canvas.drawRoundRect( + outlineOffset, + outlineOffset, + maxOf(0f, width - outlineOffset), + maxOf(0f, height - outlineOffset), + outlineRadius, + outlineRadius, + outlinePaint, + ) + } +} diff --git a/java/src/com/android/intentresolver/widget/ScrollableImagePreviewView.kt b/java/src/com/android/intentresolver/widget/ScrollableImagePreviewView.kt index c706e3ee..935a8724 100644 --- a/java/src/com/android/intentresolver/widget/ScrollableImagePreviewView.kt +++ b/java/src/com/android/intentresolver/widget/ScrollableImagePreviewView.kt @@ -71,38 +71,39 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { constructor( context: Context, attrs: AttributeSet?, - defStyleAttr: Int + defStyleAttr: Int, ) : super(context, attrs, defStyleAttr) { layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + val editButtonRoleDescription: CharSequence? context .obtainStyledAttributes(attrs, R.styleable.ScrollableImagePreviewView, defStyleAttr, 0) .use { a -> var innerSpacing = a.getDimensionPixelSize( R.styleable.ScrollableImagePreviewView_itemInnerSpacing, - -1 + -1, ) if (innerSpacing < 0) { innerSpacing = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 3f, - context.resources.displayMetrics + context.resources.displayMetrics, ) .toInt() } outerSpacing = a.getDimensionPixelSize( R.styleable.ScrollableImagePreviewView_itemOuterSpacing, - -1 + -1, ) if (outerSpacing < 0) { outerSpacing = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16f, - context.resources.displayMetrics + context.resources.displayMetrics, ) .toInt() } @@ -110,10 +111,13 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { maxWidthHint = a.getDimensionPixelSize(R.styleable.ScrollableImagePreviewView_maxWidthHint, -1) + + editButtonRoleDescription = + a.getText(R.styleable.ScrollableImagePreviewView_editButtonRoleDescription) } val itemAnimator = ItemAnimator() super.setItemAnimator(itemAnimator) - super.setAdapter(Adapter(context, itemAnimator.getAddDuration())) + super.setAdapter(Adapter(context, itemAnimator.getAddDuration(), editButtonRoleDescription)) } private var batchLoader: BatchPreviewLoader? = null @@ -125,7 +129,6 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { */ var maxWidthHint: Int = -1 - private var requestedHeight: Int = 0 private var isMeasured = false private var maxAspectRatio = MAX_ASPECT_RATIO private var maxAspectRatioString = MAX_ASPECT_RATIO_STRING @@ -217,7 +220,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { onNoPreviewCallback?.run() } previewAdapter.markLoaded() - } + }, ) maybeLoadAspectRatios() } @@ -281,24 +284,25 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { val type: PreviewType, val uri: Uri, val editAction: Runnable?, - internal var aspectRatioString: String + internal var aspectRatioString: String, ) { constructor( type: PreviewType, uri: Uri, - editAction: Runnable? + editAction: Runnable?, ) : this(type, uri, editAction, "1:1") } enum class PreviewType { Image, Video, - File + File, } private class Adapter( private val context: Context, private val fadeInDurationMs: Long, + private val editButtonRoleDescription: CharSequence?, ) : RecyclerView.Adapter<ViewHolder>() { private val previews = ArrayList<Preview>() private val imagePreviewDescription = @@ -409,6 +413,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { previewSize, fadeInDurationMs, isSharedTransitionElement = position == firstImagePos, + editButtonRoleDescription, previewReadyCallback = if ( position == firstImagePos && transitionStatusElementCallback != null @@ -416,7 +421,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { this::onTransitionElementReady } else { null - } + }, ) } } @@ -461,7 +466,8 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { previewSize: Size, fadeInDurationMs: Long, isSharedTransitionElement: Boolean, - previewReadyCallback: ((String) -> Unit)? + editButtonRoleDescription: CharSequence?, + previewReadyCallback: ((String) -> Unit)?, ) { image.setImageDrawable(null) image.alpha = 1f @@ -495,6 +501,12 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { editActionContainer?.apply { setOnClickListener { onClick.run() } visibility = View.VISIBLE + if (editButtonRoleDescription != null) { + ViewCompat.setAccessibilityDelegate( + this, + ViewRoleDescriptionAccessibilityDelegate(editButtonRoleDescription), + ) + } } } resetScope().launch { @@ -568,7 +580,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { PluralsMessageFormatter.format( itemView.context.resources, mapOf(PLURALS_COUNT to count), - R.string.other_files + R.string.other_files, ) } @@ -611,7 +623,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { state: State, viewHolder: RecyclerView.ViewHolder, changeFlags: Int, - payloads: MutableList<Any> + payloads: MutableList<Any>, ): ItemHolderInfo { return super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads).let { holderInfo -> @@ -626,7 +638,7 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { override fun animateDisappearance( viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, - postLayoutInfo: ItemHolderInfo? + postLayoutInfo: ItemHolderInfo?, ): Boolean { if (viewHolder is LoadingItemViewHolder && preLayoutInfo is LoadingItemHolderInfo) { val view = viewHolder.itemView @@ -647,10 +659,8 @@ class ScrollableImagePreviewView : RecyclerView, ImagePreviewView { super.onRemoveFinished(viewHolder) } - private inner class LoadingItemHolderInfo( - holderInfo: ItemHolderInfo, - val parentLeft: Int, - ) : ItemHolderInfo() { + private inner class LoadingItemHolderInfo(holderInfo: ItemHolderInfo, val parentLeft: Int) : + ItemHolderInfo() { init { left = holderInfo.left top = holderInfo.top diff --git a/java/src/com/android/intentresolver/widget/ViewRoleDescriptionAccessibilityDelegate.kt b/java/src/com/android/intentresolver/widget/ViewRoleDescriptionAccessibilityDelegate.kt new file mode 100644 index 00000000..8fe7144a --- /dev/null +++ b/java/src/com/android/intentresolver/widget/ViewRoleDescriptionAccessibilityDelegate.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.widget + +import android.view.View +import androidx.core.view.AccessibilityDelegateCompat +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat + +class ViewRoleDescriptionAccessibilityDelegate(private val roleDescription: CharSequence) : + AccessibilityDelegateCompat() { + override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) { + super.onInitializeAccessibilityNodeInfo(host, info) + info.roleDescription = roleDescription + } +} |