Merge "Move to even more newer and exciting Gradle 4.7 nightly." into pi-preview1-androidx-dev
am: 0c6af02fbd
Change-Id: I4adeead03675cbd89df0711705e6b7aee50dd084
diff --git a/adding-support-library-as-included-build.md b/adding-support-library-as-included-build.md
index 136929f..b77233e 100644
--- a/adding-support-library-as-included-build.md
+++ b/adding-support-library-as-included-build.md
@@ -1,5 +1,8 @@
# Adding the Support Library Build Within Another Build
+Sorry, this doesn't seem to be working at the moment.
+For now, run `./gradlew createArchive` and copy the output to where your project can use it, as described fuller in go/support-dev
+
Would you like to make a change in Support Library and have it be propagated to
your downstream Gradle build (generally an app) without having to separately
build Support Library and then build your application?
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index fcec40d..4a9a91c 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -59,6 +59,7 @@
includeProject(":lifecycle:lifecycle-livedata-core", new File(supportRoot, "lifecycle/livedata-core"))
includeProject(":lifecycle:lifecycle-livedata", new File(supportRoot, "lifecycle/livedata"))
includeProject(":lifecycle:lifecycle-reactivestreams", new File(supportRoot, "lifecycle/reactivestreams"))
+includeProject(":lifecycle:lifecycle-reactivestreams-ktx", new File(supportRoot, "lifecycle/reactivestreams/ktx"))
includeProject(":lifecycle:lifecycle-runtime", new File(supportRoot, "lifecycle/runtime"))
includeProject(":lifecycle:lifecycle-viewmodel", new File(supportRoot, "lifecycle/viewmodel"))
includeProject(":paging:integration-tests:testapp", new File(supportRoot, "paging/integration-tests/testapp"))
@@ -75,6 +76,7 @@
includeProject(":room:room-testing", new File(supportRoot, "room/testing"))
includeProject(":sqlite:sqlite", new File(supportRoot, "persistence/db"))
includeProject(":sqlite:sqlite-framework", new File(supportRoot, "persistence/db-framework"))
+includeProject(":sqlite:sqlite-ktx", new File(supportRoot, "persistence/db/ktx"))
includeProject(":jetifier-core", new File(supportRoot, "jetifier/jetifier/core"))
includeProject(":jetifier-processor", new File(supportRoot, "jetifier/jetifier/processor"))
diff --git a/car/res/values-af/strings.xml b/car/res/values-af/strings.xml
index f307fc7..1e90b40 100644
--- a/car/res/values-af/strings.xml
+++ b/car/res/values-af/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Konsentreer op die pad"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Vou knoppie in/uit"</string>
</resources>
diff --git a/car/res/values-am/strings.xml b/car/res/values-am/strings.xml
index 30ae48e..6759ee3 100644
--- a/car/res/values-am/strings.xml
+++ b/car/res/values-am/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"መንገዱ ላይ ያተኩሩ"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"አዝራርን ዘርጋ/ሰብስብ"</string>
</resources>
diff --git a/car/res/values-ar/strings.xml b/car/res/values-ar/strings.xml
index e970ed9..845908b 100644
--- a/car/res/values-ar/strings.xml
+++ b/car/res/values-ar/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"ركِّز في الطريق"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"زر التوسيع/التصغير"</string>
</resources>
diff --git a/car/res/values-az/strings.xml b/car/res/values-az/strings.xml
index e813849..b5e25dc 100644
--- a/car/res/values-az/strings.xml
+++ b/car/res/values-az/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Diqqətinizi yola yönəldin"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Düyməni genişləndirin/yığcamlaşdırın"</string>
</resources>
diff --git a/car/res/values-b+sr+Latn/strings.xml b/car/res/values-b+sr+Latn/strings.xml
index a83a82c..43dbac2 100644
--- a/car/res/values-b+sr+Latn/strings.xml
+++ b/car/res/values-b+sr+Latn/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Fokusirajte se na put"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Dugme Proširi/skupi"</string>
</resources>
diff --git a/car/res/values-be/strings.xml b/car/res/values-be/strings.xml
index 80912e4..cfef873 100644
--- a/car/res/values-be/strings.xml
+++ b/car/res/values-be/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Увага на дарогу"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Кнопка \"Разгарнуць/згарнуць\""</string>
</resources>
diff --git a/car/res/values-bg/strings.xml b/car/res/values-bg/strings.xml
index dd5811f..49e7a61 100644
--- a/car/res/values-bg/strings.xml
+++ b/car/res/values-bg/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Съсредоточете се върху пътя"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Бутон за разгъване/свиване"</string>
</resources>
diff --git a/car/res/values-bn/strings.xml b/car/res/values-bn/strings.xml
index fcf0165..4ab2ca4 100644
--- a/car/res/values-bn/strings.xml
+++ b/car/res/values-bn/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"মনোযোগ দিয়ে গাড়ি চালান"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"বোতাম বড় করুন/আড়াল করুন"</string>
</resources>
diff --git a/car/res/values-bs/strings.xml b/car/res/values-bs/strings.xml
index 99e655e..6ff5714 100644
--- a/car/res/values-bs/strings.xml
+++ b/car/res/values-bs/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Fokusirajte se na cestu"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Dugme proširi/suzi"</string>
</resources>
diff --git a/car/res/values-ca/strings.xml b/car/res/values-ca/strings.xml
index 758f6e7..8e20fd8 100644
--- a/car/res/values-ca/strings.xml
+++ b/car/res/values-ca/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentra\'t en la carretera"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botó per desplegar o replegar"</string>
</resources>
diff --git a/car/res/values-cs/strings.xml b/car/res/values-cs/strings.xml
index 27090ef..1b4cad2 100644
--- a/car/res/values-cs/strings.xml
+++ b/car/res/values-cs/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Soustřeďte se na silnici"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Tlačítko rozbalení/sbalení"</string>
</resources>
diff --git a/car/res/values-da/strings.xml b/car/res/values-da/strings.xml
index 96cc062..4a0e180 100644
--- a/car/res/values-da/strings.xml
+++ b/car/res/values-da/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Hold øjnene på vejen"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Knappen Udvid/skjul"</string>
</resources>
diff --git a/car/res/values-de/strings.xml b/car/res/values-de/strings.xml
index ee289ac..f670bd6 100644
--- a/car/res/values-de/strings.xml
+++ b/car/res/values-de/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Achte auf den Verkehr"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Schaltfläche zum Maximieren/Minimieren"</string>
</resources>
diff --git a/car/res/values-el/strings.xml b/car/res/values-el/strings.xml
index cf8ab8f..4010875 100644
--- a/car/res/values-el/strings.xml
+++ b/car/res/values-el/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Επικεντρωθείτε στον δρόμο"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Κουμπί ανάπτυξης/σύμπτυξης"</string>
</resources>
diff --git a/car/res/values-en-rAU/strings.xml b/car/res/values-en-rAU/strings.xml
index 8cc3360..a307f7e 100644
--- a/car/res/values-en-rAU/strings.xml
+++ b/car/res/values-en-rAU/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Focus on the road"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Expand/collapse button"</string>
</resources>
diff --git a/car/res/values-en-rCA/strings.xml b/car/res/values-en-rCA/strings.xml
index 8cc3360..a307f7e 100644
--- a/car/res/values-en-rCA/strings.xml
+++ b/car/res/values-en-rCA/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Focus on the road"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Expand/collapse button"</string>
</resources>
diff --git a/car/res/values-en-rGB/strings.xml b/car/res/values-en-rGB/strings.xml
index 8cc3360..a307f7e 100644
--- a/car/res/values-en-rGB/strings.xml
+++ b/car/res/values-en-rGB/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Focus on the road"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Expand/collapse button"</string>
</resources>
diff --git a/car/res/values-en-rIN/strings.xml b/car/res/values-en-rIN/strings.xml
index 8cc3360..a307f7e 100644
--- a/car/res/values-en-rIN/strings.xml
+++ b/car/res/values-en-rIN/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Focus on the road"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Expand/collapse button"</string>
</resources>
diff --git a/car/res/values-en-rXC/strings.xml b/car/res/values-en-rXC/strings.xml
index c11c1cb..16d0daf 100644
--- a/car/res/values-en-rXC/strings.xml
+++ b/car/res/values-en-rXC/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Focus on the road"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Expand/collapse button"</string>
</resources>
diff --git a/car/res/values-es-rUS/strings.xml b/car/res/values-es-rUS/strings.xml
index 6ac20d7..46a5e11 100644
--- a/car/res/values-es-rUS/strings.xml
+++ b/car/res/values-es-rUS/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concéntrate en el camino"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botón Expandir/contraer"</string>
</resources>
diff --git a/car/res/values-es/strings.xml b/car/res/values-es/strings.xml
index 09a493f..33ed10b 100644
--- a/car/res/values-es/strings.xml
+++ b/car/res/values-es/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Céntrate en la carretera"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botón para mostrar u ocultar"</string>
</resources>
diff --git a/car/res/values-et/strings.xml b/car/res/values-et/strings.xml
index 66f6e48..7561817 100644
--- a/car/res/values-et/strings.xml
+++ b/car/res/values-et/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Keskenduge teele"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Nupp Laienda/Ahenda"</string>
</resources>
diff --git a/car/res/values-eu/strings.xml b/car/res/values-eu/strings.xml
index 3773f71..0e92575 100644
--- a/car/res/values-eu/strings.xml
+++ b/car/res/values-eu/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Jarri arreta errepidean"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Zabaltzeko/Tolesteko botoia"</string>
</resources>
diff --git a/car/res/values-fa/strings.xml b/car/res/values-fa/strings.xml
index 8668d6b..59152f0 100644
--- a/car/res/values-fa/strings.xml
+++ b/car/res/values-fa/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"روی جاده تمرکز داشته باشید"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"دکمه بزرگ کردن/کوچک کردن"</string>
</resources>
diff --git a/car/res/values-fi/strings.xml b/car/res/values-fi/strings.xml
index e93cb9c..5bbb440 100644
--- a/car/res/values-fi/strings.xml
+++ b/car/res/values-fi/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Pidä katse tiessä"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Laajennus- ja tiivistyspainike"</string>
</resources>
diff --git a/car/res/values-fr-rCA/strings.xml b/car/res/values-fr-rCA/strings.xml
index f32315b..e90ddf2 100644
--- a/car/res/values-fr-rCA/strings.xml
+++ b/car/res/values-fr-rCA/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentrez-vous sur la route"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Bouton Développer/Réduire"</string>
</resources>
diff --git a/car/res/values-fr/strings.xml b/car/res/values-fr/strings.xml
index f32315b..e90ddf2 100644
--- a/car/res/values-fr/strings.xml
+++ b/car/res/values-fr/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentrez-vous sur la route"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Bouton Développer/Réduire"</string>
</resources>
diff --git a/car/res/values-gl/strings.xml b/car/res/values-gl/strings.xml
index 42bc515..c89cc35 100644
--- a/car/res/values-gl/strings.xml
+++ b/car/res/values-gl/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Céntrate na estrada"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botón despregar/contraer"</string>
</resources>
diff --git a/car/res/values-gu/strings.xml b/car/res/values-gu/strings.xml
index f215ec2..4397f5d 100644
--- a/car/res/values-gu/strings.xml
+++ b/car/res/values-gu/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"રસ્તા પર ફોકસ કરો"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"વિસ્તાર કરો/સંકુચિત કરો બટન"</string>
</resources>
diff --git a/car/res/values-hi/strings.xml b/car/res/values-hi/strings.xml
index 53f8a8d..08d973b 100644
--- a/car/res/values-hi/strings.xml
+++ b/car/res/values-hi/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"सड़क पर ध्यान दें"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"बड़ा/छोटा करने वाला बटन"</string>
</resources>
diff --git a/car/res/values-hr/strings.xml b/car/res/values-hr/strings.xml
index 0a08dcc..5714327 100644
--- a/car/res/values-hr/strings.xml
+++ b/car/res/values-hr/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Usredotočite se na cestu"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Gumb za proširivanje/sažimanje"</string>
</resources>
diff --git a/car/res/values-hu/strings.xml b/car/res/values-hu/strings.xml
index 3618719..88c2577 100644
--- a/car/res/values-hu/strings.xml
+++ b/car/res/values-hu/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Figyeljen az útra"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Gomb kibontása/összecsukása"</string>
</resources>
diff --git a/car/res/values-hy/strings.xml b/car/res/values-hy/strings.xml
index 3a85b41..d97cd0a 100644
--- a/car/res/values-hy/strings.xml
+++ b/car/res/values-hy/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Հետևեք ճանապարհին"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"«Ծավալել/ծալել» կոճակ"</string>
</resources>
diff --git a/car/res/values-in/strings.xml b/car/res/values-in/strings.xml
index 81daf29..faf2f43 100644
--- a/car/res/values-in/strings.xml
+++ b/car/res/values-in/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Konsentrasi saat mengemudi"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Tombol luaskan/ciutkan"</string>
</resources>
diff --git a/car/res/values-is/strings.xml b/car/res/values-is/strings.xml
index 4b5d26d..7c2dd76 100644
--- a/car/res/values-is/strings.xml
+++ b/car/res/values-is/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Einbeittu þér að akstrinum"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Hnappur til að stækka/minnka"</string>
</resources>
diff --git a/car/res/values-it/strings.xml b/car/res/values-it/strings.xml
index b395409..d2b3f4f 100644
--- a/car/res/values-it/strings.xml
+++ b/car/res/values-it/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentrati sulla strada"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Pulsante Espandi/Comprimi"</string>
</resources>
diff --git a/car/res/values-iw/strings.xml b/car/res/values-iw/strings.xml
index ab92be5..79bad9c 100644
--- a/car/res/values-iw/strings.xml
+++ b/car/res/values-iw/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"עליך להתמקד בכביש"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"לחצן הרחבה וכיווץ"</string>
</resources>
diff --git a/car/res/values-ja/strings.xml b/car/res/values-ja/strings.xml
index 89fce3b..87deba0 100644
--- a/car/res/values-ja/strings.xml
+++ b/car/res/values-ja/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"運転に集中してください"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"展開 / 折りたたみボタン"</string>
</resources>
diff --git a/car/res/values-ka/strings.xml b/car/res/values-ka/strings.xml
index e3f2e07..b525a9b 100644
--- a/car/res/values-ka/strings.xml
+++ b/car/res/values-ka/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"კონცენტრირდით გზაზე"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ღილაკის გაფართოება/ჩაკეცვა"</string>
</resources>
diff --git a/car/res/values-kk/strings.xml b/car/res/values-kk/strings.xml
index bbccd56..ff327d3 100644
--- a/car/res/values-kk/strings.xml
+++ b/car/res/values-kk/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Жолға назар аударыңыз"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"\"Жаю/Жию\" түймесі"</string>
</resources>
diff --git a/car/res/values-km/strings.xml b/car/res/values-km/strings.xml
index 50fe2db..f9d9111 100644
--- a/car/res/values-km/strings.xml
+++ b/car/res/values-km/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"ផ្តោតលើការបើកបរ"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ប៊ូតុងពង្រីក/បង្រួម"</string>
</resources>
diff --git a/car/res/values-kn/strings.xml b/car/res/values-kn/strings.xml
index 6562ee2..50ba985 100644
--- a/car/res/values-kn/strings.xml
+++ b/car/res/values-kn/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"ರಸ್ತೆಯ ಮೇಲೆ ಗಮನಹರಿಸಿ"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ವಿಸ್ತರಿಸಿ/ಕುಗ್ಗಿಸಿ ಬಟನ್"</string>
</resources>
diff --git a/car/res/values-ko/strings.xml b/car/res/values-ko/strings.xml
index ac5865a..0081b5c 100644
--- a/car/res/values-ko/strings.xml
+++ b/car/res/values-ko/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"도로 상황에 집중하세요."</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"펼치기/접기 버튼"</string>
</resources>
diff --git a/car/res/values-ky/strings.xml b/car/res/values-ky/strings.xml
index 3640239..8a752fa 100644
--- a/car/res/values-ky/strings.xml
+++ b/car/res/values-ky/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Жолго көңүл буруңуз"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Жайып көрсөтүү/жыйыштыруу баскычы"</string>
</resources>
diff --git a/car/res/values-lo/strings.xml b/car/res/values-lo/strings.xml
index 4af3152..02afe60 100644
--- a/car/res/values-lo/strings.xml
+++ b/car/res/values-lo/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"ຕັ້ງໃຈຂັບລົດ"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ປຸ່ມຫຍໍ້/ຂະຫຍາຍ"</string>
</resources>
diff --git a/car/res/values-lt/strings.xml b/car/res/values-lt/strings.xml
index 685bbe5..5281e18 100644
--- a/car/res/values-lt/strings.xml
+++ b/car/res/values-lt/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Sutelkite dėmesį į kelią"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Mygtukas „Išskleisti / sutraukti“"</string>
</resources>
diff --git a/car/res/values-lv/strings.xml b/car/res/values-lv/strings.xml
index 417d331..7b4372b 100644
--- a/car/res/values-lv/strings.xml
+++ b/car/res/values-lv/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Pievērsieties autovadīšanai"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Izvēršanas/sakļaušanas poga"</string>
</resources>
diff --git a/car/res/values-mk/strings.xml b/car/res/values-mk/strings.xml
index 7377299..5184503 100644
--- a/car/res/values-mk/strings.xml
+++ b/car/res/values-mk/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Фокусирајте се на патот"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Копче за проширување/собирање"</string>
</resources>
diff --git a/car/res/values-ml/strings.xml b/car/res/values-ml/strings.xml
index d5ad91d..8047b4c 100644
--- a/car/res/values-ml/strings.xml
+++ b/car/res/values-ml/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"റോഡിൽ ശ്രദ്ധിക്കുക"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"വികസിപ്പിക്കുക/ചുരുക്കുക ബട്ടൺ"</string>
</resources>
diff --git a/car/res/values-mn/strings.xml b/car/res/values-mn/strings.xml
index 4b249a4..a8ef13c 100644
--- a/car/res/values-mn/strings.xml
+++ b/car/res/values-mn/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Зам дээр төвлөрөх"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Дэлгэх/буулгах товчлуур"</string>
</resources>
diff --git a/car/res/values-mr/strings.xml b/car/res/values-mr/strings.xml
index c79f3f3..3032c97 100644
--- a/car/res/values-mr/strings.xml
+++ b/car/res/values-mr/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"रस्त्यावर फोकस करा"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"बटण विस्तृत करा/कोलॅप्स करा"</string>
</resources>
diff --git a/car/res/values-ms/strings.xml b/car/res/values-ms/strings.xml
index d209113..301f7eb 100644
--- a/car/res/values-ms/strings.xml
+++ b/car/res/values-ms/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Beri tumpuan pada jalan raya"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Butang kembangkan/runtuhkan"</string>
</resources>
diff --git a/car/res/values-my/strings.xml b/car/res/values-my/strings.xml
index 438729a..f5317f7 100644
--- a/car/res/values-my/strings.xml
+++ b/car/res/values-my/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"လမ်းကို အာရုံစိုက်ရန်"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ချဲ့ရန်/ခေါက်သိမ်းရန် ခလုတ်"</string>
</resources>
diff --git a/car/res/values-nb/strings.xml b/car/res/values-nb/strings.xml
index eb3a144..e4c4810 100644
--- a/car/res/values-nb/strings.xml
+++ b/car/res/values-nb/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Fokuser på veien"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Vis/skjul-knapp"</string>
</resources>
diff --git a/car/res/values-ne/strings.xml b/car/res/values-ne/strings.xml
index d066c0b..c4499b8 100644
--- a/car/res/values-ne/strings.xml
+++ b/car/res/values-ne/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"सडकमा ध्यान केन्द्रित गर्नु…"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"विस्तृत/संक्षिप्त गर्ने बटन"</string>
</resources>
diff --git a/car/res/values-nl/strings.xml b/car/res/values-nl/strings.xml
index 7fcb11e..de08f63 100644
--- a/car/res/values-nl/strings.xml
+++ b/car/res/values-nl/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Houd je aandacht op de weg"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Knop voor uitvouwen/samenvouwen"</string>
</resources>
diff --git a/car/res/values-pa/strings.xml b/car/res/values-pa/strings.xml
index 137bd2a..63a19f3 100644
--- a/car/res/values-pa/strings.xml
+++ b/car/res/values-pa/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"ਸੜਕ \'ਤੇ ਧਿਆਨ ਦਿਓ"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ਵਿਸਤਾਰ ਕਰੋ/ਸਮੇਟੋ ਬਟਨ"</string>
</resources>
diff --git a/car/res/values-pl/strings.xml b/car/res/values-pl/strings.xml
index c5aa323..133d27e 100644
--- a/car/res/values-pl/strings.xml
+++ b/car/res/values-pl/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Skup się na drodze"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Przycisk zwijania/rozwijania"</string>
</resources>
diff --git a/car/res/values-pt-rBR/strings.xml b/car/res/values-pt-rBR/strings.xml
index a6c515a..6c6e459 100644
--- a/car/res/values-pt-rBR/strings.xml
+++ b/car/res/values-pt-rBR/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Foco na estrada"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botão \"Expandir/Recolher\""</string>
</resources>
diff --git a/car/res/values-pt-rPT/strings.xml b/car/res/values-pt-rPT/strings.xml
index 2338efe..911b120 100644
--- a/car/res/values-pt-rPT/strings.xml
+++ b/car/res/values-pt-rPT/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentre-se na estrada."</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botão Expandir/reduzir"</string>
</resources>
diff --git a/car/res/values-pt/strings.xml b/car/res/values-pt/strings.xml
index a6c515a..6c6e459 100644
--- a/car/res/values-pt/strings.xml
+++ b/car/res/values-pt/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Foco na estrada"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Botão \"Expandir/Recolher\""</string>
</resources>
diff --git a/car/res/values-ro/strings.xml b/car/res/values-ro/strings.xml
index 20cc3e7..7fe204a 100644
--- a/car/res/values-ro/strings.xml
+++ b/car/res/values-ro/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Concentrați-vă asupra drumului"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Butonul de extindere/restrângere"</string>
</resources>
diff --git a/car/res/values-ru/strings.xml b/car/res/values-ru/strings.xml
index 198f7fa..6554dc3 100644
--- a/car/res/values-ru/strings.xml
+++ b/car/res/values-ru/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Следите за дорогой"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Кнопка \"Развернуть/свернуть\""</string>
</resources>
diff --git a/car/res/values-si/strings.xml b/car/res/values-si/strings.xml
index 530c12a..48977bd 100644
--- a/car/res/values-si/strings.xml
+++ b/car/res/values-si/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"මාර්ගයට අවධානය යොමු කරන්න"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"දිග හැරීමේ/හැකිළීමේ බොත්තම"</string>
</resources>
diff --git a/car/res/values-sk/strings.xml b/car/res/values-sk/strings.xml
index a959d09..d8175c4 100644
--- a/car/res/values-sk/strings.xml
+++ b/car/res/values-sk/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Sústreďte sa na cestu"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Tlačidlo rozbalenia/zbalenia"</string>
</resources>
diff --git a/car/res/values-sl/strings.xml b/car/res/values-sl/strings.xml
index c0a8164..85f2602 100644
--- a/car/res/values-sl/strings.xml
+++ b/car/res/values-sl/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Glejte na cesto"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Gumb za razširitev/strnitev"</string>
</resources>
diff --git a/car/res/values-sq/strings.xml b/car/res/values-sq/strings.xml
index c8c91ef..87cb023 100644
--- a/car/res/values-sq/strings.xml
+++ b/car/res/values-sq/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Përqendrohu te rruga"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Butoni i zgjerimit/palosjes"</string>
</resources>
diff --git a/car/res/values-sr/strings.xml b/car/res/values-sr/strings.xml
index d7a6b85..d3fe526 100644
--- a/car/res/values-sr/strings.xml
+++ b/car/res/values-sr/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Фокусирајте се на пут"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Дугме Прошири/скупи"</string>
</resources>
diff --git a/car/res/values-sv/strings.xml b/car/res/values-sv/strings.xml
index 3798509..c4ae438 100644
--- a/car/res/values-sv/strings.xml
+++ b/car/res/values-sv/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Fokusera på körningen"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Knappen Utöka/komprimera"</string>
</resources>
diff --git a/car/res/values-sw/strings.xml b/car/res/values-sw/strings.xml
index a5b76c7..d3472f5 100644
--- a/car/res/values-sw/strings.xml
+++ b/car/res/values-sw/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Tia makini barabarani"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Kitufe cha kupanua/kukunja"</string>
</resources>
diff --git a/car/res/values-ta/strings.xml b/car/res/values-ta/strings.xml
index e97f385..e13ede5 100644
--- a/car/res/values-ta/strings.xml
+++ b/car/res/values-ta/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"வாகனம் ஓட்டும்போது கவனம் தேவை"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"விரிவாக்குவதற்கான/சுருக்குவதற்கான பட்டன்"</string>
</resources>
diff --git a/car/res/values-te/strings.xml b/car/res/values-te/strings.xml
index 9079a09..b106363 100644
--- a/car/res/values-te/strings.xml
+++ b/car/res/values-te/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"రహదారిపై దృష్టి ఉంచండి"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"విస్తరించు/కుదించు బటన్"</string>
</resources>
diff --git a/car/res/values-th/strings.xml b/car/res/values-th/strings.xml
index 3ba0d6b..05be415 100644
--- a/car/res/values-th/strings.xml
+++ b/car/res/values-th/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"จดจ่อกับถนน"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ปุ่มขยาย/ยุบ"</string>
</resources>
diff --git a/car/res/values-tl/strings.xml b/car/res/values-tl/strings.xml
index 395e555..a56c37c 100644
--- a/car/res/values-tl/strings.xml
+++ b/car/res/values-tl/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Tumuon sa kalsada"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Button na i-expand/i-collapse"</string>
</resources>
diff --git a/car/res/values-tr/strings.xml b/car/res/values-tr/strings.xml
index a0f0b22..b28b66e 100644
--- a/car/res/values-tr/strings.xml
+++ b/car/res/values-tr/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Dikkatinizi yola verin"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Genişlet/daralt düğmesi"</string>
</resources>
diff --git a/car/res/values-uk/strings.xml b/car/res/values-uk/strings.xml
index 2657348..1964936 100644
--- a/car/res/values-uk/strings.xml
+++ b/car/res/values-uk/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Зосередьтеся на дорозі"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Кнопка \"Розгорнути або згорнути\""</string>
</resources>
diff --git a/car/res/values-ur/strings.xml b/car/res/values-ur/strings.xml
index 60c578c..c1d6b3e 100644
--- a/car/res/values-ur/strings.xml
+++ b/car/res/values-ur/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"سڑک پر توجہ مرکوز کریں"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"پھیلائیں/سکیڑیں بٹن"</string>
</resources>
diff --git a/car/res/values-uz/strings.xml b/car/res/values-uz/strings.xml
index bdaba48..a830b84 100644
--- a/car/res/values-uz/strings.xml
+++ b/car/res/values-uz/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Diqqatingizni yo‘lga qarating"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Yoyish/yig‘ish tugmasi"</string>
</resources>
diff --git a/car/res/values-vi/strings.xml b/car/res/values-vi/strings.xml
index 37457d4..144f41a 100644
--- a/car/res/values-vi/strings.xml
+++ b/car/res/values-vi/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Tập trung vào đường đi"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Nút mở rộng/thu gọn"</string>
</resources>
diff --git a/car/res/values-zh-rCN/strings.xml b/car/res/values-zh-rCN/strings.xml
index ec0c3c4..0c2caea 100644
--- a/car/res/values-zh-rCN/strings.xml
+++ b/car/res/values-zh-rCN/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"请专心驾驶"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"“展开”/“收起”按钮"</string>
</resources>
diff --git a/car/res/values-zh-rHK/strings.xml b/car/res/values-zh-rHK/strings.xml
index 61102cc..09c1a9e 100644
--- a/car/res/values-zh-rHK/strings.xml
+++ b/car/res/values-zh-rHK/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"請專心駕駛"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"展開/收合按鈕"</string>
</resources>
diff --git a/car/res/values-zh-rTW/strings.xml b/car/res/values-zh-rTW/strings.xml
index 61102cc..09c1a9e 100644
--- a/car/res/values-zh-rTW/strings.xml
+++ b/car/res/values-zh-rTW/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"請專心駕駛"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"展開/收合按鈕"</string>
</resources>
diff --git a/car/res/values-zu/strings.xml b/car/res/values-zu/strings.xml
index bdf7337..9403834 100644
--- a/car/res/values-zu/strings.xml
+++ b/car/res/values-zu/strings.xml
@@ -17,4 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="speed_bump_lockout_message" msgid="5405697774899378511">"Gxila emgwaqweni"</string>
+ <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"Inkinobho yokunweba/ukugoqa"</string>
</resources>
diff --git a/collection/ktx/OWNERS b/collection/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/collection/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/collection/ktx/build.gradle b/collection/ktx/build.gradle
new file mode 100644
index 0000000..83944f2
--- /dev/null
+++ b/collection/ktx/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+ id("SupportKotlinLibraryPlugin")
+}
+
+dependencies {
+ compile(project(":collection"))
+ compile(KOTLIN_STDLIB)
+ testCompile(JUNIT)
+ testCompile(TRUTH)
+ testCompile(project(":internal-testutils-ktx"))
+}
+
+supportLibrary {
+ name = "Collections Kotlin Extensions"
+ publish = true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.COLLECTION
+ inceptionYear = "2018"
+ description = "Kotlin extensions for 'collection' artifact"
+}
diff --git a/collection/ktx/src/main/java/androidx/collection/ArrayMap.kt b/collection/ktx/src/main/java/androidx/collection/ArrayMap.kt
new file mode 100644
index 0000000..8f3299e
--- /dev/null
+++ b/collection/ktx/src/main/java/androidx/collection/ArrayMap.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.collection
+
+/** Returns an empty new [ArrayMap]. */
+inline fun <K, V> arrayMapOf(): ArrayMap<K, V> = ArrayMap()
+
+/**
+ * Returns a new [ArrayMap] with the specified contents, given as a list of pairs where the first
+ * component is the key and the second component is the value.
+ *
+ * If multiple pairs have the same key, the resulting map will contain the value from the last of
+ * those pairs.
+ */
+fun <K, V> arrayMapOf(vararg pairs: Pair<K, V>): ArrayMap<K, V> {
+ val map = ArrayMap<K, V>(pairs.size)
+ for (pair in pairs) {
+ map[pair.first] = pair.second
+ }
+ return map
+}
diff --git a/collection/ktx/src/main/java/androidx/collection/ArraySet.kt b/collection/ktx/src/main/java/androidx/collection/ArraySet.kt
new file mode 100644
index 0000000..07b4be7
--- /dev/null
+++ b/collection/ktx/src/main/java/androidx/collection/ArraySet.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.collection
+
+/** Returns an empty new [ArraySet]. */
+inline fun <T> arraySetOf(): ArraySet<T> = ArraySet()
+
+/** Returns a new [ArraySet] with the specified contents. */
+fun <T> arraySetOf(vararg values: T): ArraySet<T> {
+ val set = ArraySet<T>(values.size)
+ @Suppress("LoopToCallChain") // Causes needless copy to a list.
+ for (value in values) {
+ set.add(value)
+ }
+ return set
+}
diff --git a/collection/ktx/src/main/java/androidx/collection/LongSparseArray.kt b/collection/ktx/src/main/java/androidx/collection/LongSparseArray.kt
new file mode 100644
index 0000000..fd6201a
--- /dev/null
+++ b/collection/ktx/src/main/java/androidx/collection/LongSparseArray.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.collection
+
+/** Returns the number of key/value pairs in the collection. */
+inline val <T> LongSparseArray<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun <T> LongSparseArray<T>.contains(key: Long) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun <T> LongSparseArray<T>.plus(other: LongSparseArray<T>): LongSparseArray<T> {
+ val new = LongSparseArray<T>(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun <T> LongSparseArray<T>.containsKey(key: Long) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun <T> LongSparseArray<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun <T> LongSparseArray<T>.getOrDefault(key: Long, defaultValue: T) =
+ get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun <T> LongSparseArray<T>.getOrElse(key: Long, defaultValue: () -> T) =
+ get(key) ?: defaultValue()
+
+/** Return true when the collection contains elements. */
+inline fun <T> LongSparseArray<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun <T> LongSparseArray<T>.remove(key: Long, value: T): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun <T> LongSparseArray<T>.putAll(other: LongSparseArray<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun <T> LongSparseArray<T>.forEach(action: (key: Long, value: T) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+fun <T> LongSparseArray<T>.keyIterator(): LongIterator = object : LongIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextLong() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun <T> LongSparseArray<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun next() = valueAt(index++)
+}
diff --git a/collection/ktx/src/main/java/androidx/collection/LruCache.kt b/collection/ktx/src/main/java/androidx/collection/LruCache.kt
new file mode 100644
index 0000000..554a012
--- /dev/null
+++ b/collection/ktx/src/main/java/androidx/collection/LruCache.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 androidx.collection
+
+/**
+ * Creates an [LruCache] with the given parameters.
+ *
+ * @param maxSize for caches that do not specify [sizeOf], this is
+ * the maximum number of entries in the cache. For all other caches,
+ * this is the maximum sum of the sizes of the entries in this cache.
+ * @param sizeOf function that returns the size of the entry for key and value in
+ * user-defined units. The default implementation returns 1.
+ * @param create a create called after a cache miss to compute a value for the corresponding key.
+ * Returns the computed value or null if no value can be computed. The default implementation
+ * returns null.
+ * @param onEntryRemoved a function called for entries that have been evicted or removed.
+ *
+ * @see LruCache.sizeOf
+ * @see LruCache.create
+ * @see LruCache.entryRemoved
+ */
+inline fun <K : Any, V : Any> lruCache(
+ maxSize: Int,
+ crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
+ @Suppress("USELESS_CAST") // https://youtrack.jetbrains.com/issue/KT-21946
+ crossinline create: (key: K) -> V? = { null as V? },
+ crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
+ { _, _, _, _ -> }
+): LruCache<K, V> {
+ return object : LruCache<K, V>(maxSize) {
+ override fun sizeOf(key: K, value: V) = sizeOf(key, value)
+ override fun create(key: K) = create(key)
+ override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
+ onEntryRemoved(evicted, key, oldValue, newValue)
+ }
+ }
+}
diff --git a/collection/ktx/src/main/java/androidx/collection/SparseArray.kt b/collection/ktx/src/main/java/androidx/collection/SparseArray.kt
new file mode 100644
index 0000000..e1d2382
--- /dev/null
+++ b/collection/ktx/src/main/java/androidx/collection/SparseArray.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.collection
+
+/** Returns the number of key/value pairs in the collection. */
+inline val <T> SparseArrayCompat<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun <T> SparseArrayCompat<T>.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun <T> SparseArrayCompat<T>.set(key: Int, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun <T> SparseArrayCompat<T>.plus(other: SparseArrayCompat<T>): SparseArrayCompat<T> {
+ val new = SparseArrayCompat<T>(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun <T> SparseArrayCompat<T>.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun <T> SparseArrayCompat<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun <T> SparseArrayCompat<T>.getOrDefault(key: Int, defaultValue: T) =
+ get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun <T> SparseArrayCompat<T>.getOrElse(key: Int, defaultValue: () -> T) =
+ get(key) ?: defaultValue()
+
+/** Return true when the collection contains elements. */
+inline fun <T> SparseArrayCompat<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun <T> SparseArrayCompat<T>.remove(key: Int, value: T): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun <T> SparseArrayCompat<T>.putAll(other: SparseArrayCompat<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun <T> SparseArrayCompat<T>.forEach(action: (key: Int, value: T) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+fun <T> SparseArrayCompat<T>.keyIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun <T> SparseArrayCompat<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun next() = valueAt(index++)
+}
diff --git a/collection/ktx/src/test/java/androidx/collection/ArrayMapTest.kt b/collection/ktx/src/test/java/androidx/collection/ArrayMapTest.kt
new file mode 100644
index 0000000..8c002dd
--- /dev/null
+++ b/collection/ktx/src/test/java/androidx/collection/ArrayMapTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 androidx.collection
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ArrayMapTest {
+ @Test fun empty() {
+ val map = arrayMapOf<String, String>()
+ assertEquals(0, map.size)
+ }
+
+ @Test fun nonEmpty() {
+ val map = arrayMapOf("foo" to "bar", "bar" to "baz")
+ assertThat(map).containsExactly("foo", "bar", "bar", "baz")
+ }
+
+ @Test fun duplicateKeyKeepsLast() {
+ val map = arrayMapOf("foo" to "bar", "foo" to "baz")
+ assertThat(map).containsExactly("foo", "baz")
+ }
+}
diff --git a/collection/ktx/src/test/java/androidx/collection/ArraySetTest.kt b/collection/ktx/src/test/java/androidx/collection/ArraySetTest.kt
new file mode 100644
index 0000000..71a561d
--- /dev/null
+++ b/collection/ktx/src/test/java/androidx/collection/ArraySetTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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 androidx.collection
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ArraySetTest {
+ @Test fun empty() {
+ val set = arraySetOf<String>()
+ assertEquals(0, set.size)
+ }
+
+ @Test fun nonEmpty() {
+ val set = arraySetOf("foo", "bar", "baz")
+ assertThat(set).containsExactly("foo", "bar", "baz")
+ }
+}
diff --git a/collection/ktx/src/test/java/androidx/collection/LongSparseArrayTest.kt b/collection/ktx/src/test/java/androidx/collection/LongSparseArrayTest.kt
new file mode 100644
index 0000000..3419664
--- /dev/null
+++ b/collection/ktx/src/test/java/androidx/collection/LongSparseArrayTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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 androidx.collection
+
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class LongSparseArrayTest {
+ @Test fun sizeProperty() {
+ val array = LongSparseArray<String>()
+ assertEquals(0, array.size)
+ array.put(1L, "one")
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = LongSparseArray<String>()
+ assertFalse(1L in array)
+ array.put(1L, "one")
+ assertTrue(1L in array)
+ }
+
+ @Test fun containsOperatorWithValue() {
+ val array = LongSparseArray<String>()
+
+ array.put(1L, "one")
+ assertFalse(2L in array)
+
+ array.put(2L, "two")
+ assertTrue(2L in array)
+ }
+
+ @Test fun setOperator() {
+ val array = LongSparseArray<String>()
+ array[1L] = "one"
+ assertEquals("one", array.get(1L))
+ }
+
+ @Test fun plusOperator() {
+ val first = LongSparseArray<String>().apply { put(1L, "one") }
+ val second = LongSparseArray<String>().apply { put(2L, "two") }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1L, combined.keyAt(0))
+ assertEquals("one", combined.valueAt(0))
+ assertEquals(2L, combined.keyAt(1))
+ assertEquals("two", combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.containsKey(1L))
+ array.put(1L, "one")
+ assertTrue(array.containsKey(1L))
+ }
+
+ @Test fun containsKeyWithValue() {
+ val array = LongSparseArray<String>()
+
+ array.put(1L, "one")
+ assertFalse(array.containsKey(2L))
+
+ array.put(2L, "one")
+ assertTrue(array.containsKey(2L))
+ }
+
+ @Test fun containsValue() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.containsValue("one"))
+ array.put(1L, "one")
+ assertTrue(array.containsValue("one"))
+ }
+
+ @Test fun getOrDefault() {
+ val array = LongSparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrDefault(1L, default))
+ array.put(1L, "one")
+ assertEquals("one", array.getOrDefault(1L, default))
+ }
+
+ @Test fun getOrElse() {
+ val array = LongSparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrElse(1L) { default })
+ array.put(1L, "one")
+ assertEquals("one", array.getOrElse(1L) { fail() })
+ }
+
+ @Test fun isNotEmpty() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.isNotEmpty())
+ array.put(1L, "one")
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = LongSparseArray<String>()
+ array.put(1L, "one")
+ assertFalse(array.remove(0L, "one"))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1L, "two"))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1L, "one"))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = LongSparseArray<String>()
+ val source = LongSparseArray<String>()
+ source.put(1L, "one")
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = LongSparseArray<String>()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val keys = mutableListOf<Long>()
+ val values = mutableListOf<String>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1L, 2L, 6L)
+ assertThat(values).containsExactly("one", "two", "six")
+ }
+
+ @Test fun keyIterator() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1L, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(2L, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(6L, iterator.nextLong())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals("one", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("two", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("six", iterator.next())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/collection/ktx/src/test/java/androidx/collection/LruCacheTest.kt b/collection/ktx/src/test/java/androidx/collection/LruCacheTest.kt
new file mode 100644
index 0000000..c47b423
--- /dev/null
+++ b/collection/ktx/src/test/java/androidx/collection/LruCacheTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 androidx.collection
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class LruCacheTest {
+ private data class TestData(val x: String = "bla")
+
+ @Test fun size() {
+ val cache = lruCache<String, TestData>(200, { k, (x) -> k.length * x.length })
+ cache.put("long", TestData())
+ assertEquals(cache.size(), 12)
+ }
+
+ @Test fun create() {
+ val cache = lruCache<String, TestData>(200, create = { key -> TestData("$key foo") })
+ assertEquals(cache.get("kung"), TestData("kung foo"))
+ }
+
+ @Test fun onEntryRemoved() {
+ var wasCalled = false
+
+ val cache = lruCache<String, TestData>(200, onEntryRemoved = { _, _, _, _ ->
+ wasCalled = true
+ })
+ val initial = TestData()
+ cache.put("a", initial)
+ cache.remove("a")
+ assertTrue(wasCalled)
+ }
+}
diff --git a/collection/ktx/src/test/java/androidx/collection/SparseArrayTest.kt b/collection/ktx/src/test/java/androidx/collection/SparseArrayTest.kt
new file mode 100644
index 0000000..90d46c8
--- /dev/null
+++ b/collection/ktx/src/test/java/androidx/collection/SparseArrayTest.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 androidx.collection
+
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseArrayCompatTest {
+ @Test fun sizeProperty() {
+ val array = SparseArrayCompat<String>()
+ assertEquals(0, array.size)
+ array.put(1, "one")
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(1 in array)
+ array.put(1, "one")
+ assertTrue(1 in array)
+ }
+
+ @Test fun containsOperatorWithItem() {
+ val array = SparseArrayCompat<String>()
+
+ array.put(1, "one")
+ assertFalse(2 in array)
+
+ array.put(2, "two")
+ assertTrue(2 in array)
+ }
+
+ @Test fun setOperator() {
+ val array = SparseArrayCompat<String>()
+ array[1] = "one"
+ assertEquals("one", array.get(1))
+ }
+
+ @Test fun plusOperator() {
+ val first = SparseArrayCompat<String>().apply { put(1, "one") }
+ val second = SparseArrayCompat<String>().apply { put(2, "two") }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1, combined.keyAt(0))
+ assertEquals("one", combined.valueAt(0))
+ assertEquals(2, combined.keyAt(1))
+ assertEquals("two", combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(array.containsKey(1))
+ array.put(1, "one")
+ assertTrue(array.containsKey(1))
+ }
+
+ @Test fun containsValue() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(array.containsValue("one"))
+ array.put(1, "one")
+ assertTrue(array.containsValue("one"))
+ }
+
+ @Test fun getOrDefault() {
+ val array = SparseArrayCompat<Any>()
+ val default = Any()
+ assertSame(default, array.getOrDefault(1, default))
+ array.put(1, "one")
+ assertEquals("one", array.getOrDefault(1, default))
+ }
+
+ @Test fun getOrElse() {
+ val array = SparseArrayCompat<Any>()
+ val default = Any()
+ assertSame(default, array.getOrElse(1) { default })
+ array.put(1, "one")
+ assertEquals("one", array.getOrElse(1) { fail() })
+ }
+
+ @Test fun isNotEmpty() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(array.isNotEmpty())
+ array.put(1, "one")
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = SparseArrayCompat<String>()
+ array.put(1, "one")
+ assertFalse(array.remove(0, "one"))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1, "two"))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1, "one"))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = SparseArrayCompat<String>()
+ val source = SparseArrayCompat<String>()
+ source.put(1, "one")
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = SparseArrayCompat<String>()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val keys = mutableListOf<Int>()
+ val values = mutableListOf<String>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1, 2, 6)
+ assertThat(values).containsExactly("one", "two", "six")
+ }
+
+ @Test fun keyIterator() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(2, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(6, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = SparseArrayCompat<String>()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals("one", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("two", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("six", iterator.next())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/compat/res/values/dimens.xml b/compat/res/values/dimens.xml
index 1dcff5e..41abbe6 100644
--- a/compat/res/values/dimens.xml
+++ b/compat/res/values/dimens.xml
@@ -70,4 +70,10 @@
<!-- the paddingtop on the right side of the notification (for time etc.) -->
<dimen name="notification_right_side_padding_top">2dp</dimen>
+
+ <!-- the maximum width of the large icon, above which it will be downscaled -->
+ <dimen name="notification_icon_max_width">320dp</dimen>
+
+ <!-- the maximum height of the large icon, above which it will be downscaled -->
+ <dimen name="notification_icon_max_height">320dp</dimen>
</resources>
diff --git a/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java b/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
index 75ad1d5..2ca517f 100644
--- a/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
@@ -32,6 +32,7 @@
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -258,7 +259,7 @@
public void testBitmapIconCompat_getType() {
IconCompat icon = IconCompat.createWithBitmap(Bitmap.createBitmap(16, 16,
Bitmap.Config.ARGB_8888));
- assertEquals(IconCompat.TYPE_BITMAP, icon.getType());
+ assertEquals(Icon.TYPE_BITMAP, icon.getType());
}
@Test
@@ -266,7 +267,7 @@
byte[] data = new byte[4];
data[0] = data[1] = data[2] = data[3] = (byte) 255;
IconCompat icon = IconCompat.createWithData(data, 0, 4);
- assertEquals(IconCompat.TYPE_DATA, icon.getType());
+ assertEquals(Icon.TYPE_DATA, icon.getType());
}
@Test
@@ -278,11 +279,11 @@
String filePath = file.toURI().getPath();
IconCompat icon = IconCompat.createWithContentUri(Uri.fromFile(file));
- assertEquals(IconCompat.TYPE_URI, icon.getType());
+ assertEquals(Icon.TYPE_URI, icon.getType());
assertEquals(filePath, icon.getUri().getPath());
icon = IconCompat.createWithContentUri(file.toURI().toString());
- assertEquals(IconCompat.TYPE_URI, icon.getType());
+ assertEquals(Icon.TYPE_URI, icon.getType());
assertEquals(filePath, icon.getUri().getPath());
} finally {
file.delete();
@@ -292,7 +293,7 @@
@Test
public void testResourceIconCompat_getType() {
IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.bmp_test);
- assertEquals(IconCompat.TYPE_RESOURCE, icon.getType());
+ assertEquals(Icon.TYPE_RESOURCE, icon.getType());
assertEquals("androidx.core.test", icon.getResPackage());
assertEquals(R.drawable.bmp_test, icon.getResId());
}
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompat.java b/compat/src/main/java/androidx/core/app/NotificationCompat.java
index b7d88eb..6d1ce46 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompat.java
@@ -963,11 +963,37 @@
* Set the large icon that is shown in the ticker and notification.
*/
public Builder setLargeIcon(Bitmap icon) {
- mLargeIcon = icon;
+ mLargeIcon = reduceLargeIconSize(icon);
return this;
}
/**
+ * Reduce the size of a notification icon if it's overly large. The framework does
+ * this automatically starting from API 27.
+ */
+ private Bitmap reduceLargeIconSize(Bitmap icon) {
+ if (icon == null || Build.VERSION.SDK_INT >= 27) {
+ return icon;
+ }
+
+ Resources res = mContext.getResources();
+ int maxWidth = res.getDimensionPixelSize(R.dimen.notification_icon_max_width);
+ int maxHeight = res.getDimensionPixelSize(R.dimen.notification_icon_max_height);
+ if (icon.getWidth() <= maxWidth && icon.getHeight() <= maxHeight) {
+ return icon;
+ }
+
+ double scale = Math.min(
+ maxWidth / (double) Math.max(1, icon.getWidth()),
+ maxHeight / (double) Math.max(1, icon.getHeight()));
+ return Bitmap.createScaledBitmap(
+ icon,
+ (int) Math.ceil(icon.getWidth() * scale),
+ (int) Math.ceil(icon.getHeight() * scale),
+ true /* filtered */);
+ }
+
+ /**
* Set the sound to play. It will play on the default stream.
*
* <p>
diff --git a/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java b/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
index cd9968c..366ddf3 100644
--- a/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
+++ b/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
@@ -16,6 +16,12 @@
package androidx.core.graphics.drawable;
+import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_DATA;
+import static android.graphics.drawable.Icon.TYPE_RESOURCE;
+import static android.graphics.drawable.Icon.TYPE_URI;
+
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
@@ -57,6 +63,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
+import androidx.core.os.BuildCompat;
import java.io.File;
import java.io.FileInputStream;
@@ -79,34 +86,6 @@
*/
public static final int TYPE_UNKOWN = -1;
- // TODO: Switch these to the public constants in the beta branch.
- /**
- * @hide
- */
- @RestrictTo(LIBRARY)
- public static final int TYPE_BITMAP = 1;
- /**
- * @hide
- */
- @RestrictTo(LIBRARY)
- public static final int TYPE_RESOURCE = 2;
- /**
- * @hide
- */
- @RestrictTo(LIBRARY)
- public static final int TYPE_DATA = 3;
- /**
- * @hide
- */
- @RestrictTo(LIBRARY)
- public static final int TYPE_URI = 4;
- /**
- * @hide
- */
- @RestrictTo(LIBRARY)
- public static final int TYPE_ADAPTIVE_BITMAP = 5;
-
-
/**
* @hide
*/
@@ -645,8 +624,10 @@
*/
@IconType
@RequiresApi(23)
- public static int getType(Icon icon) {
- // TODO: Switch to public APIs on P+ in beta branch.
+ public static int getType(@NonNull Icon icon) {
+ if (BuildCompat.isAtLeastP()) {
+ return icon.getType();
+ }
try {
return (int) icon.getClass().getMethod("getType").invoke(icon);
} catch (IllegalAccessException e) {
@@ -671,8 +652,10 @@
*/
@Nullable
@RequiresApi(23)
- public static String getResPackage(Icon icon) {
- // TODO: Switch to public APIs on P+ in beta branch.
+ public static String getResPackage(@NonNull Icon icon) {
+ if (BuildCompat.isAtLeastP()) {
+ return icon.getResPackage();
+ }
try {
return (String) icon.getClass().getMethod("getResPackage").invoke(icon);
} catch (IllegalAccessException e) {
@@ -697,8 +680,10 @@
*/
@IdRes
@RequiresApi(23)
- public static int getResId(Icon icon) {
- // TODO: Switch to public APIs on P+ in beta branch.
+ public static int getResId(@NonNull Icon icon) {
+ if (BuildCompat.isAtLeastP()) {
+ return icon.getResId();
+ }
try {
return (int) icon.getClass().getMethod("getResId").invoke(icon);
} catch (IllegalAccessException e) {
@@ -723,8 +708,10 @@
*/
@Nullable
@RequiresApi(23)
- public Uri getUri(Icon icon) {
- // TODO: Switch to public APIs on P+ in beta branch.
+ public Uri getUri(@NonNull Icon icon) {
+ if (BuildCompat.isAtLeastP()) {
+ return icon.getUri();
+ }
try {
return (Uri) icon.getClass().getMethod("getUri").invoke(icon);
} catch (IllegalAccessException e) {
diff --git a/core/ktx/OWNERS b/core/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/core/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/core/ktx/api/0.1.txt b/core/ktx/api/0.1.txt
new file mode 100644
index 0000000..e324352
--- /dev/null
+++ b/core/ktx/api/0.1.txt
@@ -0,0 +1,673 @@
+package androidx.animation {
+
+ public final class AnimatorKt {
+ ctor public AnimatorKt();
+ method public static final android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+ method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+ method public static final android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static final android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static final android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static final android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.content {
+
+ public final class ContentValuesKt {
+ ctor public ContentValuesKt();
+ method public static final error.NonExistentClass contentValuesOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class ContextKt {
+ ctor public ContextKt();
+ method public static final void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ method public static final void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ }
+
+ public final class SharedPreferencesKt {
+ ctor public SharedPreferencesKt();
+ method public static final void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.content.res {
+
+ public final class TypedArrayKt {
+ ctor public TypedArrayKt();
+ method public static final boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @ColorInt public static final int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static final int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static final int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @RequiresApi(26) public static final android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static final CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ }
+
+}
+
+package androidx.database {
+
+ public final class CursorKt {
+ ctor public CursorKt();
+ method public static final byte[] getBlob(android.database.Cursor, String columnName);
+ method public static final byte[]? getBlobOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+ method public static final double getDouble(android.database.Cursor, String columnName);
+ method public static final Double? getDoubleOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+ method public static final float getFloat(android.database.Cursor, String columnName);
+ method public static final Float? getFloatOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+ method public static final int getInt(android.database.Cursor, String columnName);
+ method public static final Integer? getIntOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+ method public static final long getLong(android.database.Cursor, String columnName);
+ method public static final Long? getLongOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+ method public static final short getShort(android.database.Cursor, String columnName);
+ method public static final Short? getShortOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+ method public static final String getString(android.database.Cursor, String columnName);
+ method public static final String? getStringOrNull(android.database.Cursor, int index);
+ method public static final error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+ }
+
+}
+
+package androidx.database.sqlite {
+
+ public final class SQLiteDatabaseKt {
+ ctor public SQLiteDatabaseKt();
+ method public static final <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+ }
+
+}
+
+package androidx.graphics {
+
+ public final class BitmapKt {
+ ctor public BitmapKt();
+ method public static final android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static final android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method @RequiresApi(26) public static final android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+ method public static final operator int get(android.graphics.Bitmap, int x, int y);
+ method public static final android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+ method public static final operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+ }
+
+ public final class CanvasKt {
+ ctor public CanvasKt();
+ method public static final void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static final void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static final void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static final void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static final void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class ColorKt {
+ ctor public ColorKt();
+ method @RequiresApi(26) public static final operator float component1(android.graphics.Color);
+ method public static final operator int component1(int);
+ method @RequiresApi(26) public static final operator float component1(long);
+ method @RequiresApi(26) public static final operator float component2(android.graphics.Color);
+ method public static final operator int component2(int);
+ method @RequiresApi(26) public static final operator float component2(long);
+ method @RequiresApi(26) public static final operator float component3(android.graphics.Color);
+ method public static final operator int component3(int);
+ method @RequiresApi(26) public static final operator float component3(long);
+ method @RequiresApi(26) public static final operator float component4(android.graphics.Color);
+ method public static final operator int component4(int);
+ method @RequiresApi(26) public static final operator float component4(long);
+ method public static final int getAlpha(int);
+ method @RequiresApi(26) public static final float getAlpha(long);
+ method public static final int getBlue(int);
+ method @RequiresApi(26) public static final float getBlue(long);
+ method @RequiresApi(26) public static final android.graphics.ColorSpace getColorSpace(long);
+ method public static final int getGreen(int);
+ method @RequiresApi(26) public static final float getGreen(long);
+ method @RequiresApi(26) public static final float getLuminance(int);
+ method @RequiresApi(26) public static final float getLuminance(long);
+ method public static final int getRed(int);
+ method @RequiresApi(26) public static final float getRed(long);
+ method @RequiresApi(26) public static final boolean isSrgb(long);
+ method @RequiresApi(26) public static final boolean isWideGamut(long);
+ method @RequiresApi(26) public static final operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+ method @RequiresApi(26) public static final android.graphics.Color toColor(int);
+ method @RequiresApi(26) public static final android.graphics.Color toColor(long);
+ method @RequiresApi(26) @ColorInt public static final int toColorInt(long);
+ method @RequiresApi(26) @ColorLong public static final long toColorLong(int);
+ }
+
+ public final class MatrixKt {
+ ctor public MatrixKt();
+ method public static final error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+ method public static final error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+ method public static final operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+ method public static final error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+ method public static final error.NonExistentClass values(android.graphics.Matrix);
+ }
+
+ public final class PathKt {
+ ctor public PathKt();
+ method @RequiresApi(19) public static final infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(26) public static final Iterable<androidx.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+ method @RequiresApi(19) public static final operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static final infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static final operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static final infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+ }
+
+ public final class PathSegment {
+ ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public final android.graphics.PointF component1();
+ method public final float component2();
+ method public final android.graphics.PointF component3();
+ method public final float component4();
+ method public final androidx.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public final android.graphics.PointF getEnd();
+ method public final float getEndFraction();
+ method public final android.graphics.PointF getStart();
+ method public final float getStartFraction();
+ }
+
+ public final class PictureKt {
+ ctor public PictureKt();
+ method public static final android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class PointKt {
+ ctor public PointKt();
+ method public static final operator int component1(android.graphics.Point);
+ method public static final operator float component1(android.graphics.PointF);
+ method public static final operator int component2(android.graphics.Point);
+ method public static final operator float component2(android.graphics.PointF);
+ method public static final operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+ method public static final operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+ method public static final operator android.graphics.Point minus(android.graphics.Point, int xy);
+ method public static final operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+ method public static final operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+ method public static final operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+ method public static final operator android.graphics.Point plus(android.graphics.Point, int xy);
+ method public static final operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+ method public static final android.graphics.Point toPoint(android.graphics.PointF);
+ method public static final android.graphics.PointF toPointF(android.graphics.Point);
+ method public static final operator android.graphics.Point unaryMinus(android.graphics.Point);
+ method public static final operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+ }
+
+ public final class PorterDuffKt {
+ ctor public PorterDuffKt();
+ method public static final android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+ method public static final android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+ }
+
+ public final class RectKt {
+ ctor public RectKt();
+ method public static final infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+ method public static final infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+ method public static final operator int component1(android.graphics.Rect);
+ method public static final operator float component1(android.graphics.RectF);
+ method public static final operator int component2(android.graphics.Rect);
+ method public static final operator float component2(android.graphics.RectF);
+ method public static final operator int component3(android.graphics.Rect);
+ method public static final operator float component3(android.graphics.RectF);
+ method public static final operator int component4(android.graphics.Rect);
+ method public static final operator float component4(android.graphics.RectF);
+ method public static final operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+ method public static final operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+ method public static final operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+ method public static final operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+ method public static final operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+ method public static final operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+ method public static final operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+ method public static final operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static final infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+ method public static final infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+ method public static final operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+ method public static final operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+ method public static final operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+ method public static final operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+ method public static final operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+ method public static final operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static final android.graphics.Rect toRect(android.graphics.RectF);
+ method public static final android.graphics.RectF toRectF(android.graphics.Rect);
+ method public static final android.graphics.Region toRegion(android.graphics.Rect);
+ method public static final android.graphics.Region toRegion(android.graphics.RectF);
+ method public static final error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+ method public static final infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+ method public static final infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+ }
+
+ public final class RegionKt {
+ ctor public RegionKt();
+ method public static final infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+ method public static final infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+ method public static final operator boolean contains(android.graphics.Region, android.graphics.Point p);
+ method public static final void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+ method public static final operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+ method public static final operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+ method public static final operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+ method public static final operator android.graphics.Region not(android.graphics.Region);
+ method public static final infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+ method public static final infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+ method public static final operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+ method public static final operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+ method public static final operator android.graphics.Region unaryMinus(android.graphics.Region);
+ method public static final infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+ method public static final infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+ }
+
+ public final class ShaderKt {
+ ctor public ShaderKt();
+ method public static final void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+ }
+
+}
+
+package androidx.graphics.drawable {
+
+ public final class BitmapDrawableKt {
+ ctor public BitmapDrawableKt();
+ method public static final android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+ }
+
+ public final class ColorDrawableKt {
+ ctor public ColorDrawableKt();
+ method public static final android.graphics.drawable.ColorDrawable toDrawable(int);
+ method @RequiresApi(26) public static final android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+ }
+
+ public final class DrawableKt {
+ ctor public DrawableKt();
+ method public static final android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, int width = "intrinsicWidth", int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+ method public static final void updateBounds(android.graphics.drawable.Drawable, int left = "bounds.left", int top = "bounds.top", int right = "bounds.right", int bottom = "bounds.bottom");
+ }
+
+ public final class IconKt {
+ ctor public IconKt();
+ method @RequiresApi(26) public static final android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(android.net.Uri);
+ method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(byte[]);
+ }
+
+}
+
+package androidx.net {
+
+ public final class UriKt {
+ ctor public UriKt();
+ method public static final android.net.Uri toUri(String);
+ }
+
+}
+
+package androidx.os {
+
+ public final class BundleKt {
+ ctor public BundleKt();
+ method public static final error.NonExistentClass bundleOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class HandlerKt {
+ ctor public HandlerKt();
+ method public static final error.NonExistentClass postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static final void postDelayed(android.os.Handler, Runnable runnable, Object? token, long delayInMillis);
+ method public static final error.NonExistentClass postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static final error.NonExistentClass postDelayed(android.os.Handler, long amount, java.util.concurrent.TimeUnit unit, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method @RequiresApi(26) public static final error.NonExistentClass postDelayed(android.os.Handler, java.time.Duration duration, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ }
+
+ public final class PersistableBundleKt {
+ ctor public PersistableBundleKt();
+ method @RequiresApi(21) public static final error.NonExistentClass persistableBundleOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class TraceKt {
+ ctor public TraceKt();
+ method public static final <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+}
+
+package androidx.text {
+
+ public final class SpannableStringBuilderKt {
+ ctor public SpannableStringBuilderKt();
+ method public static final android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static final android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ }
+
+}
+
+package androidx.time {
+
+ public final class DayOfWeekKt {
+ ctor public DayOfWeekKt();
+ method @RequiresApi(26) public static final java.time.DayOfWeek asDayOfWeek(int);
+ method @RequiresApi(26) public static final int asInt(java.time.DayOfWeek);
+ }
+
+ public final class DurationKt {
+ ctor public DurationKt();
+ method @RequiresApi(26) public static final operator long component1(java.time.Duration);
+ method @RequiresApi(26) public static final operator int component2(java.time.Duration);
+ method @RequiresApi(26) public static final operator java.time.Duration div(java.time.Duration, long divisor);
+ method @RequiresApi(260) public static final java.time.Duration hours(int);
+ method @RequiresApi(260) public static final java.time.Duration hours(long);
+ method @RequiresApi(26) public static final java.time.Duration millis(int);
+ method @RequiresApi(26) public static final java.time.Duration millis(long);
+ method @RequiresApi(260) public static final java.time.Duration minutes(int);
+ method @RequiresApi(260) public static final java.time.Duration minutes(long);
+ method @RequiresApi(26) public static final java.time.Duration nanos(int);
+ method @RequiresApi(26) public static final java.time.Duration nanos(long);
+ method @RequiresApi(26) public static final java.time.Duration seconds(int);
+ method @RequiresApi(26) public static final java.time.Duration seconds(long);
+ method @RequiresApi(26) public static final operator java.time.Duration times(java.time.Duration, long multiplicand);
+ method @RequiresApi(26) public static final operator java.time.Duration unaryMinus(java.time.Duration);
+ }
+
+ public final class InstantKt {
+ ctor public InstantKt();
+ method @RequiresApi(26) public static final java.time.Instant asEpochMillis(long);
+ method @RequiresApi(26) public static final java.time.Instant asEpochSeconds(long);
+ method @RequiresApi(26) public static final operator long component1(java.time.Instant);
+ method @RequiresApi(26) public static final operator int component2(java.time.Instant);
+ }
+
+ public final class LocalDateKt {
+ ctor public LocalDateKt();
+ method @RequiresApi(26) public static final operator int component1(java.time.LocalDate);
+ method @RequiresApi(26) public static final operator java.time.Month component2(java.time.LocalDate);
+ method @RequiresApi(26) public static final operator int component3(java.time.LocalDate);
+ }
+
+ public final class LocalDateTimeKt {
+ ctor public LocalDateTimeKt();
+ method @RequiresApi(26) public static final operator java.time.LocalDate component1(java.time.LocalDateTime);
+ method @RequiresApi(26) public static final operator java.time.LocalTime component2(java.time.LocalDateTime);
+ }
+
+ public final class LocalTimeKt {
+ ctor public LocalTimeKt();
+ method @RequiresApi(26) public static final operator int component1(java.time.LocalTime);
+ method @RequiresApi(26) public static final operator int component2(java.time.LocalTime);
+ method @RequiresApi(26) public static final operator int component3(java.time.LocalTime);
+ method @RequiresApi(26) public static final operator int component4(java.time.LocalTime);
+ }
+
+ public final class MonthDayKt {
+ ctor public MonthDayKt();
+ method @RequiresApi(26) public static final operator java.time.Month component1(java.time.MonthDay);
+ method @RequiresApi(26) public static final operator int component2(java.time.MonthDay);
+ }
+
+ public final class MonthKt {
+ ctor public MonthKt();
+ method @RequiresApi(26) public static final int asInt(java.time.Month);
+ method @RequiresApi(26) public static final java.time.Month asMonth(int);
+ }
+
+ public final class OffsetDateTimeKt {
+ ctor public OffsetDateTimeKt();
+ method @RequiresApi(26) public static final operator java.time.LocalDateTime component1(java.time.OffsetDateTime);
+ method @RequiresApi(26) public static final operator java.time.ZoneOffset component2(java.time.OffsetDateTime);
+ }
+
+ public final class OffsetTimeKt {
+ ctor public OffsetTimeKt();
+ method @RequiresApi(26) public static final operator java.time.LocalTime component1(java.time.OffsetTime);
+ method @RequiresApi(26) public static final operator java.time.ZoneOffset component2(java.time.OffsetTime);
+ }
+
+ public final class PeriodKt {
+ ctor public PeriodKt();
+ method @RequiresApi(26) public static final operator int component1(java.time.Period);
+ method @RequiresApi(26) public static final operator int component2(java.time.Period);
+ method @RequiresApi(26) public static final operator int component3(java.time.Period);
+ method @RequiresApi(26) public static final java.time.Period days(int);
+ method @RequiresApi(26) public static final java.time.Period months(int);
+ method @RequiresApi(26) public static final operator java.time.Period times(java.time.Period, int multiplicand);
+ method @RequiresApi(26) public static final operator java.time.Period unaryMinus(java.time.Period);
+ method @RequiresApi(26) public static final java.time.Period years(int);
+ }
+
+ public final class YearKt {
+ ctor public YearKt();
+ method @RequiresApi(26) public static final int asInt(java.time.Year);
+ method @RequiresApi(26) public static final java.time.Year asYear(int);
+ }
+
+ public final class YearMonthKt {
+ ctor public YearMonthKt();
+ method @RequiresApi(26) public static final operator int component1(java.time.YearMonth);
+ method @RequiresApi(26) public static final operator java.time.Month component2(java.time.YearMonth);
+ }
+
+ public final class ZonedDateTimeKt {
+ ctor public ZonedDateTimeKt();
+ method @RequiresApi(26) public static final operator java.time.LocalDateTime component1(java.time.ZonedDateTime);
+ method @RequiresApi(26) public static final operator java.time.ZoneId component2(java.time.ZonedDateTime);
+ }
+
+}
+
+package androidx.transition {
+
+ public final class TransitionKt {
+ ctor public TransitionKt();
+ method @RequiresApi(19) public static final void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+ method @RequiresApi(19) public static final void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static final void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static final void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static final void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static final void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.util {
+
+ public final class ArrayMapKt {
+ ctor public ArrayMapKt();
+ method @RequiresApi(19) public static final <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+ method @RequiresApi(19) public static final <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+ }
+
+ public final class ArraySetKt {
+ ctor public ArraySetKt();
+ method @RequiresApi(23) public static final <T> android.util.ArraySet<T> arraySetOf();
+ method @RequiresApi(23) public static final <T> android.util.ArraySet<T> arraySetOf(T... values);
+ }
+
+ public final class AtomicFileKt {
+ ctor public AtomicFileKt();
+ method @RequiresApi(17) public static final byte[] readBytes(android.util.AtomicFile);
+ method @RequiresApi(17) public static final String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ method @RequiresApi(17) public static final void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+ method @RequiresApi(17) public static final void writeBytes(android.util.AtomicFile, byte[] array);
+ method @RequiresApi(17) public static final void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ }
+
+ public final class HalfKt {
+ ctor public HalfKt();
+ method @RequiresApi(26) public static final android.util.Half toHalf(short);
+ method @RequiresApi(26) public static final android.util.Half toHalf(float);
+ method @RequiresApi(26) public static final android.util.Half toHalf(double);
+ method @RequiresApi(26) public static final android.util.Half toHalf(String);
+ }
+
+ public final class LongSparseArrayKt {
+ ctor public LongSparseArrayKt();
+ method @RequiresApi(16) public static final operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static final <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static final <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+ method @RequiresApi(16) public static final <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super Long,? super T,kotlin.Unit> action);
+ method @RequiresApi(16) public static final <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+ method @RequiresApi(16) public static final <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method @RequiresApi(16) public static final <T> int getSize(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static final <T> boolean isEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static final <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static final <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static final operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static final <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static final <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static final operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static final <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+ }
+
+ public final class PairKt {
+ ctor public PairKt();
+ method public static final operator <F, S> F! component1(android.util.Pair<F,S>);
+ method public static final operator <F, S> S! component2(android.util.Pair<F,S>);
+ method public static final <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+ method public static final <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+ }
+
+ public final class RangeKt {
+ ctor public RangeKt();
+ method @RequiresApi(21) public static final infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static final operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+ method @RequiresApi(21) public static final operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static final infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+ method @RequiresApi(21) public static final <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+ method @RequiresApi(21) public static final <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+ }
+
+ public final class SizeKt {
+ ctor public SizeKt();
+ method @RequiresApi(21) public static final operator int component1(android.util.Size);
+ method @RequiresApi(21) public static final operator float component1(android.util.SizeF);
+ method @RequiresApi(21) public static final operator int component2(android.util.Size);
+ method @RequiresApi(21) public static final operator float component2(android.util.SizeF);
+ }
+
+ public final class SparseArrayKt {
+ ctor public SparseArrayKt();
+ method public static final operator <T> boolean contains(android.util.SparseArray<T>, int key);
+ method public static final <T> boolean containsKey(android.util.SparseArray<T>, int key);
+ method public static final <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+ method public static final <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super Integer,? super T,kotlin.Unit> action);
+ method public static final <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+ method public static final <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public static final <T> int getSize(android.util.SparseArray<T>);
+ method public static final <T> boolean isEmpty(android.util.SparseArray<T>);
+ method public static final <T> boolean isNotEmpty(android.util.SparseArray<T>);
+ method public static final <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+ method public static final operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static final <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static final <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+ method public static final operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+ method public static final <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+ }
+
+ public final class SparseBooleanArrayKt {
+ ctor public SparseBooleanArrayKt();
+ method public static final operator boolean contains(android.util.SparseBooleanArray, int key);
+ method public static final boolean containsKey(android.util.SparseBooleanArray, int key);
+ method public static final boolean containsValue(android.util.SparseBooleanArray, boolean value);
+ method public static final void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super Integer,? super Boolean,kotlin.Unit> action);
+ method public static final boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+ method public static final error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<Boolean> defaultValue);
+ method public static final int getSize(android.util.SparseBooleanArray);
+ method public static final boolean isEmpty(android.util.SparseBooleanArray);
+ method public static final boolean isNotEmpty(android.util.SparseBooleanArray);
+ method public static final kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+ method public static final operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static final void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static final boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+ method public static final void removeAt(android.util.SparseBooleanArray, int index);
+ method public static final operator void set(android.util.SparseBooleanArray, int key, boolean value);
+ method public static final kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+ }
+
+ public final class SparseIntArrayKt {
+ ctor public SparseIntArrayKt();
+ method public static final operator boolean contains(android.util.SparseIntArray, int key);
+ method public static final boolean containsKey(android.util.SparseIntArray, int key);
+ method public static final boolean containsValue(android.util.SparseIntArray, int value);
+ method public static final void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super Integer,? super Integer,kotlin.Unit> action);
+ method public static final int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+ method public static final error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<Integer> defaultValue);
+ method public static final int getSize(android.util.SparseIntArray);
+ method public static final boolean isEmpty(android.util.SparseIntArray);
+ method public static final boolean isNotEmpty(android.util.SparseIntArray);
+ method public static final kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+ method public static final operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static final void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static final boolean remove(android.util.SparseIntArray, int key, int value);
+ method public static final operator void set(android.util.SparseIntArray, int key, int value);
+ method public static final kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+ }
+
+ public final class SparseLongArrayKt {
+ ctor public SparseLongArrayKt();
+ method @RequiresApi(18) public static final operator boolean contains(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static final boolean containsKey(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static final boolean containsValue(android.util.SparseLongArray, long value);
+ method @RequiresApi(18) public static final void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super Integer,? super Long,kotlin.Unit> action);
+ method @RequiresApi(18) public static final long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+ method @RequiresApi(18) public static final error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<Long> defaultValue);
+ method @RequiresApi(18) public static final int getSize(android.util.SparseLongArray);
+ method @RequiresApi(18) public static final boolean isEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static final boolean isNotEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static final kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+ method @RequiresApi(18) public static final operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static final void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static final boolean remove(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static final operator void set(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static final kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+ }
+
+}
+
+package androidx.view {
+
+ public final class ViewGroupKt {
+ ctor public ViewGroupKt();
+ method public static final operator boolean contains(android.view.ViewGroup, android.view.View view);
+ method public static final void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static final void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super Integer,? super android.view.View,kotlin.Unit> action);
+ method public static final operator android.view.View get(android.view.ViewGroup, int index);
+ method public static final int getSize(android.view.ViewGroup);
+ method public static final boolean isEmpty(android.view.ViewGroup);
+ method public static final boolean isNotEmpty(android.view.ViewGroup);
+ method public static final operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+ method public static final operator void minusAssign(android.view.ViewGroup, android.view.View view);
+ method public static final operator void plusAssign(android.view.ViewGroup, android.view.View view);
+ method public static final void setMargins(android.view.ViewGroup.MarginLayoutParams, int size);
+ method public static final void updateMargins(android.view.ViewGroup.MarginLayoutParams, int left = "leftMargin", int top = "topMargin", int right = "rightMargin", int bottom = "bottomMargin");
+ method @RequiresApi(17) public static final void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, int start = "marginStart", int top = "topMargin", int end = "marginEnd", int bottom = "bottomMargin");
+ }
+
+ public final class ViewKt {
+ ctor public ViewKt();
+ method public static final void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static final void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static final void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static final Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method @RequiresApi(16) public static final Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static final void setPadding(android.view.View, int size);
+ method public static final android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method public static final void updatePadding(android.view.View, int left = "paddingLeft", int top = "paddingTop", int right = "paddingRight", int bottom = "paddingBottom");
+ method @RequiresApi(17) public static final void updatePaddingRelative(android.view.View, int start = "paddingStart", int top = "paddingTop", int end = "paddingEnd", int bottom = "paddingBottom");
+ }
+
+}
+
diff --git a/core/ktx/api/0.2.txt b/core/ktx/api/0.2.txt
new file mode 100644
index 0000000..5fbfe40
--- /dev/null
+++ b/core/ktx/api/0.2.txt
@@ -0,0 +1,742 @@
+package androidx.animation {
+
+ public final class AnimatorKt {
+ ctor public AnimatorKt();
+ method public static android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+ method public static android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.content {
+
+ public final class ContentValuesKt {
+ ctor public ContentValuesKt();
+ method public static error.NonExistentClass contentValuesOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class ContextKt {
+ ctor public ContextKt();
+ method public static void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ method public static void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ }
+
+ public final class SharedPreferencesKt {
+ ctor public SharedPreferencesKt();
+ method public static void edit(android.content.SharedPreferences, boolean commit = "false", kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.content.res {
+
+ public final class TypedArrayKt {
+ ctor public TypedArrayKt();
+ method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+ }
+
+}
+
+package androidx.database {
+
+ public final class CursorKt {
+ ctor public CursorKt();
+ method public static byte[] getBlob(android.database.Cursor, String columnName);
+ method public static byte[]? getBlobOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+ method public static double getDouble(android.database.Cursor, String columnName);
+ method public static Double? getDoubleOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+ method public static float getFloat(android.database.Cursor, String columnName);
+ method public static Float? getFloatOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+ method public static int getInt(android.database.Cursor, String columnName);
+ method public static Integer? getIntOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+ method public static long getLong(android.database.Cursor, String columnName);
+ method public static Long? getLongOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+ method public static short getShort(android.database.Cursor, String columnName);
+ method public static Short? getShortOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+ method public static String getString(android.database.Cursor, String columnName);
+ method public static String? getStringOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+ }
+
+}
+
+package androidx.database.sqlite {
+
+ public final class SQLiteDatabaseKt {
+ ctor public SQLiteDatabaseKt();
+ method public static <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+ }
+
+}
+
+package androidx.graphics {
+
+ public final class BitmapKt {
+ ctor public BitmapKt();
+ method public static android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method @RequiresApi(26) public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+ method public static operator int get(android.graphics.Bitmap, int x, int y);
+ method public static android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+ method public static operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+ }
+
+ public final class CanvasKt {
+ ctor public CanvasKt();
+ method public static void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class ColorKt {
+ ctor public ColorKt();
+ method @RequiresApi(26) public static operator float component1(android.graphics.Color);
+ method public static operator int component1(int);
+ method @RequiresApi(26) public static operator float component1(long);
+ method @RequiresApi(26) public static operator float component2(android.graphics.Color);
+ method public static operator int component2(int);
+ method @RequiresApi(26) public static operator float component2(long);
+ method @RequiresApi(26) public static operator float component3(android.graphics.Color);
+ method public static operator int component3(int);
+ method @RequiresApi(26) public static operator float component3(long);
+ method @RequiresApi(26) public static operator float component4(android.graphics.Color);
+ method public static operator int component4(int);
+ method @RequiresApi(26) public static operator float component4(long);
+ method public static int getAlpha(int);
+ method @RequiresApi(26) public static float getAlpha(long);
+ method public static int getBlue(int);
+ method @RequiresApi(26) public static float getBlue(long);
+ method @RequiresApi(26) public static android.graphics.ColorSpace getColorSpace(long);
+ method public static int getGreen(int);
+ method @RequiresApi(26) public static float getGreen(long);
+ method @RequiresApi(26) public static float getLuminance(int);
+ method @RequiresApi(26) public static float getLuminance(long);
+ method public static int getRed(int);
+ method @RequiresApi(26) public static float getRed(long);
+ method @RequiresApi(26) public static boolean isSrgb(long);
+ method @RequiresApi(26) public static boolean isWideGamut(long);
+ method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+ method @RequiresApi(26) public static android.graphics.Color toColor(int);
+ method @RequiresApi(26) public static android.graphics.Color toColor(long);
+ method @RequiresApi(26) @ColorInt public static int toColorInt(long);
+ method @ColorInt public static int toColorInt(String);
+ method @RequiresApi(26) @ColorLong public static long toColorLong(int);
+ }
+
+ public final class MatrixKt {
+ ctor public MatrixKt();
+ method public static error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+ method public static error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+ method public static operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+ method public static error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+ method public static error.NonExistentClass values(android.graphics.Matrix);
+ }
+
+ public final class PathKt {
+ ctor public PathKt();
+ method @RequiresApi(19) public static infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(26) public static Iterable<androidx.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+ method @RequiresApi(19) public static operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+ }
+
+ public final class PathSegment {
+ ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public android.graphics.PointF component1();
+ method public float component2();
+ method public android.graphics.PointF component3();
+ method public float component4();
+ method public androidx.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public android.graphics.PointF getEnd();
+ method public float getEndFraction();
+ method public android.graphics.PointF getStart();
+ method public float getStartFraction();
+ }
+
+ public final class PictureKt {
+ ctor public PictureKt();
+ method public static android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class PointKt {
+ ctor public PointKt();
+ method public static operator int component1(android.graphics.Point);
+ method public static operator float component1(android.graphics.PointF);
+ method public static operator int component2(android.graphics.Point);
+ method public static operator float component2(android.graphics.PointF);
+ method public static operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+ method public static operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+ method public static operator android.graphics.Point minus(android.graphics.Point, int xy);
+ method public static operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+ method public static operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+ method public static operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+ method public static operator android.graphics.Point plus(android.graphics.Point, int xy);
+ method public static operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+ method public static android.graphics.Point toPoint(android.graphics.PointF);
+ method public static android.graphics.PointF toPointF(android.graphics.Point);
+ method public static operator android.graphics.Point unaryMinus(android.graphics.Point);
+ method public static operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+ }
+
+ public final class PorterDuffKt {
+ ctor public PorterDuffKt();
+ method public static android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+ method public static android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+ }
+
+ public final class RectKt {
+ ctor public RectKt();
+ method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator int component1(android.graphics.Rect);
+ method public static operator float component1(android.graphics.RectF);
+ method public static operator int component2(android.graphics.Rect);
+ method public static operator float component2(android.graphics.RectF);
+ method public static operator int component3(android.graphics.Rect);
+ method public static operator float component3(android.graphics.RectF);
+ method public static operator int component4(android.graphics.Rect);
+ method public static operator float component4(android.graphics.RectF);
+ method public static operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+ method public static operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+ method public static operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+ method public static operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+ method public static operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+ method public static operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+ method public static operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static android.graphics.Rect toRect(android.graphics.RectF);
+ method public static android.graphics.RectF toRectF(android.graphics.Rect);
+ method public static android.graphics.Region toRegion(android.graphics.Rect);
+ method public static android.graphics.Region toRegion(android.graphics.RectF);
+ method public static error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+ method public static infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+ }
+
+ public final class RegionKt {
+ ctor public RegionKt();
+ method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+ method public static operator boolean contains(android.graphics.Region, android.graphics.Point p);
+ method public static void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+ method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+ method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+ method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region not(android.graphics.Region);
+ method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+ method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region unaryMinus(android.graphics.Region);
+ method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+ }
+
+ public final class ShaderKt {
+ ctor public ShaderKt();
+ method public static void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+ }
+
+}
+
+package androidx.graphics.drawable {
+
+ public final class BitmapDrawableKt {
+ ctor public BitmapDrawableKt();
+ method public static android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+ }
+
+ public final class ColorDrawableKt {
+ ctor public ColorDrawableKt();
+ method public static android.graphics.drawable.ColorDrawable toDrawable(int);
+ method @RequiresApi(26) public static android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+ }
+
+ public final class DrawableKt {
+ ctor public DrawableKt();
+ method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, @Px int width = "intrinsicWidth", @Px int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+ method public static void updateBounds(android.graphics.drawable.Drawable, @Px int left = "bounds.left", @Px int top = "bounds.top", @Px int right = "bounds.right", @Px int bottom = "bounds.bottom");
+ }
+
+ public final class IconKt {
+ ctor public IconKt();
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.net.Uri);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(byte[]);
+ }
+
+}
+
+package androidx.net {
+
+ public final class UriKt {
+ ctor public UriKt();
+ method public static android.net.Uri toUri(String);
+ }
+
+}
+
+package androidx.os {
+
+ public final class BundleKt {
+ ctor public BundleKt();
+ method public static error.NonExistentClass bundleOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class FileKt {
+ ctor public FileKt();
+ method public static android.net.Uri toUri(java.io.File);
+ }
+
+ public final class HandlerKt {
+ ctor public HandlerKt();
+ method public static error.NonExistentClass postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static void postDelayed(android.os.Handler, Runnable runnable, Object? token, long delayInMillis);
+ method public static error.NonExistentClass postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static error.NonExistentClass postDelayed(android.os.Handler, long amount, java.util.concurrent.TimeUnit unit, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method @RequiresApi(26) public static error.NonExistentClass postDelayed(android.os.Handler, java.time.Duration duration, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ }
+
+ public final class PersistableBundleKt {
+ ctor public PersistableBundleKt();
+ method @RequiresApi(21) public static error.NonExistentClass persistableBundleOf(kotlin.Pair<String,?>... pairs);
+ }
+
+ public final class TraceKt {
+ ctor public TraceKt();
+ method public static <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+}
+
+package androidx.text {
+
+ public final class CharSequenceKt {
+ ctor public CharSequenceKt();
+ method public static boolean isDigitsOnly(CharSequence);
+ method public static int trimmedLength(CharSequence);
+ }
+
+ public final class SpannableStringBuilderKt {
+ ctor public SpannableStringBuilderKt();
+ method public static android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ }
+
+ public final class SpannableStringKt {
+ ctor public SpannableStringKt();
+ method public static error.NonExistentClass clearSpans(android.text.Spannable);
+ method public static operator void minusAssign(android.text.Spannable, Object span);
+ method public static operator void plusAssign(android.text.Spannable, Object span);
+ method public static android.text.Spannable toSpannable(CharSequence);
+ }
+
+ public final class SpannedStringKt {
+ ctor public SpannedStringKt();
+ method public static android.text.Spanned toSpanned(CharSequence);
+ }
+
+ public final class StringKt {
+ ctor public StringKt();
+ method public static String htmlEncode(String);
+ }
+
+}
+
+package androidx.time {
+
+ public final class DayOfWeekKt {
+ ctor public DayOfWeekKt();
+ method @RequiresApi(26) deprecated public static java.time.DayOfWeek asDayOfWeek(int);
+ method @RequiresApi(26) deprecated public static int asInt(java.time.DayOfWeek);
+ }
+
+ public final class DeprecationKt {
+ ctor public DeprecationKt();
+ }
+
+ public final class DurationKt {
+ ctor public DurationKt();
+ method @RequiresApi(26) deprecated public static operator long component1(java.time.Duration);
+ method @RequiresApi(26) deprecated public static operator int component2(java.time.Duration);
+ method @RequiresApi(26) deprecated public static operator java.time.Duration div(java.time.Duration, long divisor);
+ method @RequiresApi(26) deprecated public static java.time.Duration hours(int);
+ method @RequiresApi(26) deprecated public static java.time.Duration hours(long);
+ method @RequiresApi(26) deprecated public static java.time.Duration millis(int);
+ method @RequiresApi(26) deprecated public static java.time.Duration millis(long);
+ method @RequiresApi(26) deprecated public static java.time.Duration minutes(int);
+ method @RequiresApi(26) deprecated public static java.time.Duration minutes(long);
+ method @RequiresApi(26) deprecated public static java.time.Duration nanos(int);
+ method @RequiresApi(26) deprecated public static java.time.Duration nanos(long);
+ method @RequiresApi(26) deprecated public static java.time.Duration seconds(int);
+ method @RequiresApi(26) deprecated public static java.time.Duration seconds(long);
+ method @RequiresApi(26) deprecated public static operator java.time.Duration times(java.time.Duration, long multiplicand);
+ method @RequiresApi(26) deprecated public static operator java.time.Duration unaryMinus(java.time.Duration);
+ }
+
+ public final class InstantKt {
+ ctor public InstantKt();
+ method @RequiresApi(26) deprecated public static java.time.Instant asEpochMillis(long);
+ method @RequiresApi(26) deprecated public static java.time.Instant asEpochSeconds(long);
+ method @RequiresApi(26) deprecated public static operator long component1(java.time.Instant);
+ method @RequiresApi(26) deprecated public static operator int component2(java.time.Instant);
+ }
+
+ public final class LocalDateKt {
+ ctor public LocalDateKt();
+ method @RequiresApi(26) deprecated public static operator int component1(java.time.LocalDate);
+ method @RequiresApi(26) deprecated public static operator java.time.Month component2(java.time.LocalDate);
+ method @RequiresApi(26) deprecated public static operator int component3(java.time.LocalDate);
+ }
+
+ public final class LocalDateTimeKt {
+ ctor public LocalDateTimeKt();
+ method @RequiresApi(26) deprecated public static operator java.time.LocalDate component1(java.time.LocalDateTime);
+ method @RequiresApi(26) deprecated public static operator java.time.LocalTime component2(java.time.LocalDateTime);
+ }
+
+ public final class LocalTimeKt {
+ ctor public LocalTimeKt();
+ method @RequiresApi(26) deprecated public static operator int component1(java.time.LocalTime);
+ method @RequiresApi(26) deprecated public static operator int component2(java.time.LocalTime);
+ method @RequiresApi(26) deprecated public static operator int component3(java.time.LocalTime);
+ method @RequiresApi(26) deprecated public static operator int component4(java.time.LocalTime);
+ }
+
+ public final class MonthDayKt {
+ ctor public MonthDayKt();
+ method @RequiresApi(26) deprecated public static operator java.time.Month component1(java.time.MonthDay);
+ method @RequiresApi(26) deprecated public static operator int component2(java.time.MonthDay);
+ }
+
+ public final class MonthKt {
+ ctor public MonthKt();
+ method @RequiresApi(26) deprecated public static int asInt(java.time.Month);
+ method @RequiresApi(26) deprecated public static java.time.Month asMonth(int);
+ }
+
+ public final class OffsetDateTimeKt {
+ ctor public OffsetDateTimeKt();
+ method @RequiresApi(26) deprecated public static operator java.time.LocalDateTime component1(java.time.OffsetDateTime);
+ method @RequiresApi(26) deprecated public static operator java.time.ZoneOffset component2(java.time.OffsetDateTime);
+ }
+
+ public final class OffsetTimeKt {
+ ctor public OffsetTimeKt();
+ method @RequiresApi(26) deprecated public static operator java.time.LocalTime component1(java.time.OffsetTime);
+ method @RequiresApi(26) deprecated public static operator java.time.ZoneOffset component2(java.time.OffsetTime);
+ }
+
+ public final class PeriodKt {
+ ctor public PeriodKt();
+ method @RequiresApi(26) deprecated public static operator int component1(java.time.Period);
+ method @RequiresApi(26) deprecated public static operator int component2(java.time.Period);
+ method @RequiresApi(26) deprecated public static operator int component3(java.time.Period);
+ method @RequiresApi(26) deprecated public static java.time.Period days(int);
+ method @RequiresApi(26) deprecated public static java.time.Period months(int);
+ method @RequiresApi(26) deprecated public static operator java.time.Period times(java.time.Period, int multiplicand);
+ method @RequiresApi(26) deprecated public static operator java.time.Period unaryMinus(java.time.Period);
+ method @RequiresApi(26) deprecated public static java.time.Period years(int);
+ }
+
+ public final class YearKt {
+ ctor public YearKt();
+ method @RequiresApi(26) deprecated public static int asInt(java.time.Year);
+ method @RequiresApi(26) deprecated public static java.time.Year asYear(int);
+ }
+
+ public final class YearMonthKt {
+ ctor public YearMonthKt();
+ method @RequiresApi(26) deprecated public static operator int component1(java.time.YearMonth);
+ method @RequiresApi(26) deprecated public static operator java.time.Month component2(java.time.YearMonth);
+ }
+
+ public final class ZonedDateTimeKt {
+ ctor public ZonedDateTimeKt();
+ method @RequiresApi(26) deprecated public static operator java.time.LocalDateTime component1(java.time.ZonedDateTime);
+ method @RequiresApi(26) deprecated public static operator java.time.ZoneId component2(java.time.ZonedDateTime);
+ }
+
+}
+
+package androidx.transition {
+
+ public final class TransitionKt {
+ ctor public TransitionKt();
+ method @RequiresApi(19) public static void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+ method @RequiresApi(19) public static void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.util {
+
+ public final class ArrayMapKt {
+ ctor public ArrayMapKt();
+ method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+ method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+ }
+
+ public final class ArraySetKt {
+ ctor public ArraySetKt();
+ method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf();
+ method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf(T... values);
+ }
+
+ public final class AtomicFileKt {
+ ctor public AtomicFileKt();
+ method @RequiresApi(17) public static byte[] readBytes(android.util.AtomicFile);
+ method @RequiresApi(17) public static String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ method @RequiresApi(17) public static void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+ method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+ method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ }
+
+ public final class HalfKt {
+ ctor public HalfKt();
+ method @RequiresApi(26) public static android.util.Half toHalf(short);
+ method @RequiresApi(26) public static android.util.Half toHalf(float);
+ method @RequiresApi(26) public static android.util.Half toHalf(double);
+ method @RequiresApi(26) public static android.util.Half toHalf(String);
+ }
+
+ public final class LocaleKt {
+ ctor public LocaleKt();
+ method @RequiresApi(17) public static int getLayoutDirection(java.util.Locale);
+ }
+
+ public final class LongSparseArrayKt {
+ ctor public LongSparseArrayKt();
+ method @RequiresApi(16) public static operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+ method @RequiresApi(16) public static <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super Long,? super T,kotlin.Unit> action);
+ method @RequiresApi(16) public static <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+ method @RequiresApi(16) public static <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method @RequiresApi(16) public static <T> int getSize(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> boolean isEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+ }
+
+ public final class LruCacheKt {
+ ctor public LruCacheKt();
+ method public static <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,Integer> sizeOf = "{ _, _ -> 1 }", kotlin.jvm.functions.Function1<? super K,? extends V> create = "{ null as V? }", kotlin.jvm.functions.Function4<? super Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = "{ _, _, _, _ -> }");
+ }
+
+ public final class PairKt {
+ ctor public PairKt();
+ method public static operator <F, S> F! component1(android.util.Pair<F,S>);
+ method public static operator <F, S> S! component2(android.util.Pair<F,S>);
+ method public static <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+ method public static <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+ }
+
+ public final class RangeKt {
+ ctor public RangeKt();
+ method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+ method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+ method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+ method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+ }
+
+ public final class SizeKt {
+ ctor public SizeKt();
+ method @RequiresApi(21) public static operator int component1(android.util.Size);
+ method @RequiresApi(21) public static operator float component1(android.util.SizeF);
+ method @RequiresApi(21) public static operator int component2(android.util.Size);
+ method @RequiresApi(21) public static operator float component2(android.util.SizeF);
+ }
+
+ public final class SparseArrayKt {
+ ctor public SparseArrayKt();
+ method public static operator <T> boolean contains(android.util.SparseArray<T>, int key);
+ method public static <T> boolean containsKey(android.util.SparseArray<T>, int key);
+ method public static <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+ method public static <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super Integer,? super T,kotlin.Unit> action);
+ method public static <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+ method public static <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public static <T> int getSize(android.util.SparseArray<T>);
+ method public static <T> boolean isEmpty(android.util.SparseArray<T>);
+ method public static <T> boolean isNotEmpty(android.util.SparseArray<T>);
+ method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+ method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+ method public static operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+ method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+ }
+
+ public final class SparseBooleanArrayKt {
+ ctor public SparseBooleanArrayKt();
+ method public static operator boolean contains(android.util.SparseBooleanArray, int key);
+ method public static boolean containsKey(android.util.SparseBooleanArray, int key);
+ method public static boolean containsValue(android.util.SparseBooleanArray, boolean value);
+ method public static void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super Integer,? super Boolean,kotlin.Unit> action);
+ method public static boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+ method public static error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<Boolean> defaultValue);
+ method public static int getSize(android.util.SparseBooleanArray);
+ method public static boolean isEmpty(android.util.SparseBooleanArray);
+ method public static boolean isNotEmpty(android.util.SparseBooleanArray);
+ method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+ method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+ method public static void removeAt(android.util.SparseBooleanArray, int index);
+ method public static operator void set(android.util.SparseBooleanArray, int key, boolean value);
+ method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+ }
+
+ public final class SparseIntArrayKt {
+ ctor public SparseIntArrayKt();
+ method public static operator boolean contains(android.util.SparseIntArray, int key);
+ method public static boolean containsKey(android.util.SparseIntArray, int key);
+ method public static boolean containsValue(android.util.SparseIntArray, int value);
+ method public static void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super Integer,? super Integer,kotlin.Unit> action);
+ method public static int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+ method public static error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<Integer> defaultValue);
+ method public static int getSize(android.util.SparseIntArray);
+ method public static boolean isEmpty(android.util.SparseIntArray);
+ method public static boolean isNotEmpty(android.util.SparseIntArray);
+ method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+ method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static boolean remove(android.util.SparseIntArray, int key, int value);
+ method public static operator void set(android.util.SparseIntArray, int key, int value);
+ method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+ }
+
+ public final class SparseLongArrayKt {
+ ctor public SparseLongArrayKt();
+ method @RequiresApi(18) public static operator boolean contains(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static boolean containsKey(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static boolean containsValue(android.util.SparseLongArray, long value);
+ method @RequiresApi(18) public static void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super Integer,? super Long,kotlin.Unit> action);
+ method @RequiresApi(18) public static long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+ method @RequiresApi(18) public static error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<Long> defaultValue);
+ method @RequiresApi(18) public static int getSize(android.util.SparseLongArray);
+ method @RequiresApi(18) public static boolean isEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static boolean isNotEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+ method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static operator void set(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+ }
+
+}
+
+package androidx.view {
+
+ public final class MenuKt {
+ ctor public MenuKt();
+ method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+ method public static void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+ method public static void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super Integer,? super android.view.MenuItem,kotlin.Unit> action);
+ method public static operator android.view.MenuItem get(android.view.Menu, int index);
+ method public static int getSize(android.view.Menu);
+ method public static boolean isEmpty(android.view.Menu);
+ method public static boolean isNotEmpty(android.view.Menu);
+ method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+ }
+
+ public final class ViewGroupKt {
+ ctor public ViewGroupKt();
+ method public static operator boolean contains(android.view.ViewGroup, android.view.View view);
+ method public static void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super Integer,? super android.view.View,kotlin.Unit> action);
+ method public static operator android.view.View get(android.view.ViewGroup, int index);
+ method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+ method public static int getSize(android.view.ViewGroup);
+ method public static boolean isEmpty(android.view.ViewGroup);
+ method public static boolean isNotEmpty(android.view.ViewGroup);
+ method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+ method public static operator void minusAssign(android.view.ViewGroup, android.view.View view);
+ method public static operator void plusAssign(android.view.ViewGroup, android.view.View view);
+ method public static void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+ method public static void updateLayoutParams(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+ method public static void updateMargins(android.view.ViewGroup.MarginLayoutParams, @Px int left = "leftMargin", @Px int top = "topMargin", @Px int right = "rightMargin", @Px int bottom = "bottomMargin");
+ method @RequiresApi(17) public static void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, @Px int start = "marginStart", @Px int top = "topMargin", @Px int end = "marginEnd", @Px int bottom = "bottomMargin");
+ }
+
+ public final class ViewKt {
+ ctor public ViewKt();
+ method public static void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static boolean isGone(android.view.View);
+ method public static boolean isInvisible(android.view.View);
+ method public static boolean isVisible(android.view.View);
+ method public static Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static void setGone(android.view.View, boolean value);
+ method public static void setInvisible(android.view.View, boolean value);
+ method public static void setPadding(android.view.View, @Px int size);
+ method public static void setVisible(android.view.View, boolean value);
+ method public static android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method public static void updatePadding(android.view.View, @Px int left = "paddingLeft", @Px int top = "paddingTop", @Px int right = "paddingRight", @Px int bottom = "paddingBottom");
+ method @RequiresApi(17) public static void updatePaddingRelative(android.view.View, @Px int start = "paddingStart", @Px int top = "paddingTop", @Px int end = "paddingEnd", @Px int bottom = "paddingBottom");
+ }
+
+}
+
diff --git a/core/ktx/api/current.txt b/core/ktx/api/current.txt
new file mode 100644
index 0000000..4c56ac9
--- /dev/null
+++ b/core/ktx/api/current.txt
@@ -0,0 +1,658 @@
+package androidx.core.animation {
+
+ public final class AnimatorKt {
+ ctor public AnimatorKt();
+ method public static android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+ method public static android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ method public static android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.core.content {
+
+ public final class ContentValuesKt {
+ ctor public ContentValuesKt();
+ method public static error.NonExistentClass contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class ContextKt {
+ ctor public ContextKt();
+ method public static void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ method public static void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+ }
+
+ public final class SharedPreferencesKt {
+ ctor public SharedPreferencesKt();
+ method public static void edit(android.content.SharedPreferences, boolean commit = "false", kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.core.content.res {
+
+ public final class TypedArrayKt {
+ ctor public TypedArrayKt();
+ method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+ method public static <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+ }
+
+}
+
+package androidx.core.database {
+
+ public final class CursorKt {
+ ctor public CursorKt();
+ method public static byte[] getBlob(android.database.Cursor, String columnName);
+ method public static byte[]? getBlobOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+ method public static double getDouble(android.database.Cursor, String columnName);
+ method public static Double? getDoubleOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+ method public static float getFloat(android.database.Cursor, String columnName);
+ method public static Float? getFloatOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+ method public static int getInt(android.database.Cursor, String columnName);
+ method public static Integer? getIntOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+ method public static long getLong(android.database.Cursor, String columnName);
+ method public static Long? getLongOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+ method public static short getShort(android.database.Cursor, String columnName);
+ method public static Short? getShortOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+ method public static String getString(android.database.Cursor, String columnName);
+ method public static String? getStringOrNull(android.database.Cursor, int index);
+ method public static error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+ }
+
+}
+
+package androidx.core.database.sqlite {
+
+ public final class SQLiteDatabaseKt {
+ ctor public SQLiteDatabaseKt();
+ method public static <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+ }
+
+}
+
+package androidx.core.graphics {
+
+ public final class BitmapKt {
+ ctor public BitmapKt();
+ method public static android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method @RequiresApi(26) public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+ method public static operator int get(android.graphics.Bitmap, int x, int y);
+ method public static android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+ method public static operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+ }
+
+ public final class CanvasKt {
+ ctor public CanvasKt();
+ method public static void withMatrix(android.graphics.Canvas, android.graphics.Matrix matrix = "Matrix()", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ method public static void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class ColorKt {
+ ctor public ColorKt();
+ method @RequiresApi(26) public static operator float component1(android.graphics.Color);
+ method public static operator int component1(int);
+ method @RequiresApi(26) public static operator float component1(long);
+ method @RequiresApi(26) public static operator float component2(android.graphics.Color);
+ method public static operator int component2(int);
+ method @RequiresApi(26) public static operator float component2(long);
+ method @RequiresApi(26) public static operator float component3(android.graphics.Color);
+ method public static operator int component3(int);
+ method @RequiresApi(26) public static operator float component3(long);
+ method @RequiresApi(26) public static operator float component4(android.graphics.Color);
+ method public static operator int component4(int);
+ method @RequiresApi(26) public static operator float component4(long);
+ method public static int getAlpha(int);
+ method @RequiresApi(26) public static float getAlpha(long);
+ method public static int getBlue(int);
+ method @RequiresApi(26) public static float getBlue(long);
+ method @RequiresApi(26) public static android.graphics.ColorSpace getColorSpace(long);
+ method public static int getGreen(int);
+ method @RequiresApi(26) public static float getGreen(long);
+ method @RequiresApi(26) public static float getLuminance(int);
+ method @RequiresApi(26) public static float getLuminance(long);
+ method public static int getRed(int);
+ method @RequiresApi(26) public static float getRed(long);
+ method @RequiresApi(26) public static boolean isSrgb(long);
+ method @RequiresApi(26) public static boolean isWideGamut(long);
+ method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+ method @RequiresApi(26) public static android.graphics.Color toColor(int);
+ method @RequiresApi(26) public static android.graphics.Color toColor(long);
+ method @RequiresApi(26) @ColorInt public static int toColorInt(long);
+ method @ColorInt public static int toColorInt(String);
+ method @RequiresApi(26) @ColorLong public static long toColorLong(int);
+ }
+
+ public final class MatrixKt {
+ ctor public MatrixKt();
+ method public static error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+ method public static error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+ method public static operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+ method public static error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+ method public static error.NonExistentClass values(android.graphics.Matrix);
+ }
+
+ public final class PathKt {
+ ctor public PathKt();
+ method @RequiresApi(19) public static infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+ method @RequiresApi(19) public static operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+ method @RequiresApi(19) public static infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+ }
+
+ public final class PathSegment {
+ ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public android.graphics.PointF component1();
+ method public float component2();
+ method public android.graphics.PointF component3();
+ method public float component4();
+ method public androidx.core.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+ method public android.graphics.PointF getEnd();
+ method public float getEndFraction();
+ method public android.graphics.PointF getStart();
+ method public float getStartFraction();
+ }
+
+ public final class PictureKt {
+ ctor public PictureKt();
+ method public static android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class PointKt {
+ ctor public PointKt();
+ method public static operator int component1(android.graphics.Point);
+ method public static operator float component1(android.graphics.PointF);
+ method public static operator int component2(android.graphics.Point);
+ method public static operator float component2(android.graphics.PointF);
+ method public static operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+ method public static operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+ method public static operator android.graphics.Point minus(android.graphics.Point, int xy);
+ method public static operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+ method public static operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+ method public static operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+ method public static operator android.graphics.Point plus(android.graphics.Point, int xy);
+ method public static operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+ method public static android.graphics.Point toPoint(android.graphics.PointF);
+ method public static android.graphics.PointF toPointF(android.graphics.Point);
+ method public static operator android.graphics.Point unaryMinus(android.graphics.Point);
+ method public static operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+ }
+
+ public final class PorterDuffKt {
+ ctor public PorterDuffKt();
+ method public static android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+ method public static android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+ }
+
+ public final class RectKt {
+ ctor public RectKt();
+ method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator int component1(android.graphics.Rect);
+ method public static operator float component1(android.graphics.RectF);
+ method public static operator int component2(android.graphics.Rect);
+ method public static operator float component2(android.graphics.RectF);
+ method public static operator int component3(android.graphics.Rect);
+ method public static operator float component3(android.graphics.RectF);
+ method public static operator int component4(android.graphics.Rect);
+ method public static operator float component4(android.graphics.RectF);
+ method public static operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+ method public static operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+ method public static operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+ method public static operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+ method public static operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+ method public static operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+ method public static operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+ method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+ method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+ method public static android.graphics.Rect toRect(android.graphics.RectF);
+ method public static android.graphics.RectF toRectF(android.graphics.Rect);
+ method public static android.graphics.Region toRegion(android.graphics.Rect);
+ method public static android.graphics.Region toRegion(android.graphics.RectF);
+ method public static error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+ method public static infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+ method public static infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+ }
+
+ public final class RegionKt {
+ ctor public RegionKt();
+ method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+ method public static operator boolean contains(android.graphics.Region, android.graphics.Point p);
+ method public static void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+ method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+ method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+ method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region not(android.graphics.Region);
+ method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+ method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+ method public static operator android.graphics.Region unaryMinus(android.graphics.Region);
+ method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+ method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+ }
+
+ public final class ShaderKt {
+ ctor public ShaderKt();
+ method public static void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+ }
+
+}
+
+package androidx.core.graphics.drawable {
+
+ public final class BitmapDrawableKt {
+ ctor public BitmapDrawableKt();
+ method public static android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+ }
+
+ public final class ColorDrawableKt {
+ ctor public ColorDrawableKt();
+ method public static android.graphics.drawable.ColorDrawable toDrawable(int);
+ method @RequiresApi(26) public static android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+ }
+
+ public final class DrawableKt {
+ ctor public DrawableKt();
+ method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, @Px int width = "intrinsicWidth", @Px int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+ method public static void updateBounds(android.graphics.drawable.Drawable, @Px int left = "bounds.left", @Px int top = "bounds.top", @Px int right = "bounds.right", @Px int bottom = "bounds.bottom");
+ }
+
+ public final class IconKt {
+ ctor public IconKt();
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.net.Uri);
+ method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(byte[]);
+ }
+
+}
+
+package androidx.core.net {
+
+ public final class UriKt {
+ ctor public UriKt();
+ method public static java.io.File toFile(android.net.Uri);
+ method public static android.net.Uri toUri(String);
+ method public static android.net.Uri toUri(java.io.File);
+ }
+
+}
+
+package androidx.core.os {
+
+ public final class BundleKt {
+ ctor public BundleKt();
+ method public static error.NonExistentClass bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class HandlerKt {
+ ctor public HandlerKt();
+ method public static Runnable postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static Runnable postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ }
+
+ public final class PersistableBundleKt {
+ ctor public PersistableBundleKt();
+ method @RequiresApi(21) public static error.NonExistentClass persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class TraceKt {
+ ctor public TraceKt();
+ method public static <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+}
+
+package androidx.core.preference {
+
+ public final class PreferenceGroupKt {
+ ctor public PreferenceGroupKt();
+ method public static operator boolean contains(android.preference.PreferenceGroup, android.preference.Preference preference);
+ method public static void forEach(android.preference.PreferenceGroup, kotlin.jvm.functions.Function1<? super android.preference.Preference,kotlin.Unit> action);
+ method public static void forEachIndexed(android.preference.PreferenceGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.preference.Preference,kotlin.Unit> action);
+ method public static operator android.preference.Preference get(android.preference.PreferenceGroup, CharSequence key);
+ method public static operator android.preference.Preference get(android.preference.PreferenceGroup, int index);
+ method public static int getSize(android.preference.PreferenceGroup);
+ method public static boolean isEmpty(android.preference.PreferenceGroup);
+ method public static boolean isNotEmpty(android.preference.PreferenceGroup);
+ method public static operator java.util.Iterator<android.preference.Preference> iterator(android.preference.PreferenceGroup);
+ method public static operator void minusAssign(android.preference.PreferenceGroup, android.preference.Preference preference);
+ method public static operator void plusAssign(android.preference.PreferenceGroup, android.preference.Preference preference);
+ }
+
+}
+
+package androidx.core.text {
+
+ public final class CharSequenceKt {
+ ctor public CharSequenceKt();
+ method public static boolean isDigitsOnly(CharSequence);
+ method public static int trimmedLength(CharSequence);
+ }
+
+ public final class HtmlKt {
+ ctor public HtmlKt();
+ method public static android.text.Spanned parseAsHtml(String, int flags = "FROM_HTML_MODE_LEGACY", android.text.Html.ImageGetter? imageGetter = "null", android.text.Html.TagHandler? tagHandler = "null");
+ method public static String toHtml(android.text.Spanned, int option = "TO_HTML_PARAGRAPH_LINES_CONSECUTIVE");
+ }
+
+ public final class SpannableStringBuilderKt {
+ ctor public SpannableStringBuilderKt();
+ method public static android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ method public static android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+ }
+
+ public final class SpannableStringKt {
+ ctor public SpannableStringKt();
+ method public static error.NonExistentClass clearSpans(android.text.Spannable);
+ method public static operator void minusAssign(android.text.Spannable, Object span);
+ method public static operator void plusAssign(android.text.Spannable, Object span);
+ method public static operator void set(android.text.Spannable, int start, int end, Object span);
+ method public static operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+ method public static android.text.Spannable toSpannable(CharSequence);
+ }
+
+ public final class SpannedStringKt {
+ ctor public SpannedStringKt();
+ method public static android.text.Spanned toSpanned(CharSequence);
+ }
+
+ public final class StringKt {
+ ctor public StringKt();
+ method public static String htmlEncode(String);
+ }
+
+}
+
+package androidx.core.transition {
+
+ public final class TransitionKt {
+ ctor public TransitionKt();
+ method @RequiresApi(19) public static void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+ method @RequiresApi(19) public static void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ method @RequiresApi(19) public static void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+ }
+
+}
+
+package androidx.core.util {
+
+ public final class ArrayMapKt {
+ ctor public ArrayMapKt();
+ method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+ method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+ }
+
+ public final class ArraySetKt {
+ ctor public ArraySetKt();
+ method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf();
+ method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf(T... values);
+ }
+
+ public final class AtomicFileKt {
+ ctor public AtomicFileKt();
+ method @RequiresApi(17) public static byte[] readBytes(android.util.AtomicFile);
+ method @RequiresApi(17) public static String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ method @RequiresApi(17) public static void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+ method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+ method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+ }
+
+ public final class HalfKt {
+ ctor public HalfKt();
+ method @RequiresApi(26) public static android.util.Half toHalf(short);
+ method @RequiresApi(26) public static android.util.Half toHalf(float);
+ method @RequiresApi(26) public static android.util.Half toHalf(double);
+ method @RequiresApi(26) public static android.util.Half toHalf(String);
+ }
+
+ public final class LocaleKt {
+ ctor public LocaleKt();
+ method @RequiresApi(17) public static int getLayoutDirection(java.util.Locale);
+ }
+
+ public final class LongSparseArrayKt {
+ ctor public LongSparseArrayKt();
+ method @RequiresApi(16) public static operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+ method @RequiresApi(16) public static <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+ method @RequiresApi(16) public static <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+ method @RequiresApi(16) public static <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+ method @RequiresApi(16) public static <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method @RequiresApi(16) public static <T> int getSize(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> boolean isEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+ method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+ method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+ method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+ }
+
+ public final class LruCacheKt {
+ ctor public LruCacheKt();
+ method public static <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf = "{ _, _ -> 1 }", kotlin.jvm.functions.Function1<? super K,? extends V> create = "{ null as V? }", kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = "{ _, _, _, _ -> }");
+ }
+
+ public final class PairKt {
+ ctor public PairKt();
+ method public static operator <F, S> F! component1(android.util.Pair<F,S>);
+ method public static operator <F, S> S! component2(android.util.Pair<F,S>);
+ method public static <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+ method public static <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+ }
+
+ public final class RangeKt {
+ ctor public RangeKt();
+ method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+ method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+ method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+ method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+ method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+ }
+
+ public final class SizeKt {
+ ctor public SizeKt();
+ method @RequiresApi(21) public static operator int component1(android.util.Size);
+ method @RequiresApi(21) public static operator float component1(android.util.SizeF);
+ method @RequiresApi(21) public static operator int component2(android.util.Size);
+ method @RequiresApi(21) public static operator float component2(android.util.SizeF);
+ }
+
+ public final class SparseArrayKt {
+ ctor public SparseArrayKt();
+ method public static operator <T> boolean contains(android.util.SparseArray<T>, int key);
+ method public static <T> boolean containsKey(android.util.SparseArray<T>, int key);
+ method public static <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+ method public static <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+ method public static <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+ method public static <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public static <T> int getSize(android.util.SparseArray<T>);
+ method public static <T> boolean isEmpty(android.util.SparseArray<T>);
+ method public static <T> boolean isNotEmpty(android.util.SparseArray<T>);
+ method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+ method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+ method public static <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+ method public static operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+ method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+ }
+
+ public final class SparseBooleanArrayKt {
+ ctor public SparseBooleanArrayKt();
+ method public static operator boolean contains(android.util.SparseBooleanArray, int key);
+ method public static boolean containsKey(android.util.SparseBooleanArray, int key);
+ method public static boolean containsValue(android.util.SparseBooleanArray, boolean value);
+ method public static void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+ method public static boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+ method public static error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+ method public static int getSize(android.util.SparseBooleanArray);
+ method public static boolean isEmpty(android.util.SparseBooleanArray);
+ method public static boolean isNotEmpty(android.util.SparseBooleanArray);
+ method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+ method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+ method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+ method public static operator void set(android.util.SparseBooleanArray, int key, boolean value);
+ method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+ }
+
+ public final class SparseIntArrayKt {
+ ctor public SparseIntArrayKt();
+ method public static operator boolean contains(android.util.SparseIntArray, int key);
+ method public static boolean containsKey(android.util.SparseIntArray, int key);
+ method public static boolean containsValue(android.util.SparseIntArray, int value);
+ method public static void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+ method public static int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+ method public static error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+ method public static int getSize(android.util.SparseIntArray);
+ method public static boolean isEmpty(android.util.SparseIntArray);
+ method public static boolean isNotEmpty(android.util.SparseIntArray);
+ method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+ method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+ method public static boolean remove(android.util.SparseIntArray, int key, int value);
+ method public static operator void set(android.util.SparseIntArray, int key, int value);
+ method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+ }
+
+ public final class SparseLongArrayKt {
+ ctor public SparseLongArrayKt();
+ method @RequiresApi(18) public static operator boolean contains(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static boolean containsKey(android.util.SparseLongArray, int key);
+ method @RequiresApi(18) public static boolean containsValue(android.util.SparseLongArray, long value);
+ method @RequiresApi(18) public static void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+ method @RequiresApi(18) public static long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+ method @RequiresApi(18) public static error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+ method @RequiresApi(18) public static int getSize(android.util.SparseLongArray);
+ method @RequiresApi(18) public static boolean isEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static boolean isNotEmpty(android.util.SparseLongArray);
+ method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+ method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+ method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static operator void set(android.util.SparseLongArray, int key, long value);
+ method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+ }
+
+}
+
+package androidx.core.view {
+
+ public final class MenuKt {
+ ctor public MenuKt();
+ method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+ method public static void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+ method public static void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+ method public static operator android.view.MenuItem get(android.view.Menu, int index);
+ method public static int getSize(android.view.Menu);
+ method public static boolean isEmpty(android.view.Menu);
+ method public static boolean isNotEmpty(android.view.Menu);
+ method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+ }
+
+ public final class ViewGroupKt {
+ ctor public ViewGroupKt();
+ method public static operator boolean contains(android.view.ViewGroup, android.view.View view);
+ method public static void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+ method public static operator android.view.View get(android.view.ViewGroup, int index);
+ method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+ method public static int getSize(android.view.ViewGroup);
+ method public static boolean isEmpty(android.view.ViewGroup);
+ method public static boolean isNotEmpty(android.view.ViewGroup);
+ method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+ method public static operator void minusAssign(android.view.ViewGroup, android.view.View view);
+ method public static operator void plusAssign(android.view.ViewGroup, android.view.View view);
+ method public static void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+ method public static void updateMargins(android.view.ViewGroup.MarginLayoutParams, @Px int left = "leftMargin", @Px int top = "topMargin", @Px int right = "rightMargin", @Px int bottom = "bottomMargin");
+ method @RequiresApi(17) public static void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, @Px int start = "marginStart", @Px int top = "topMargin", @Px int end = "marginEnd", @Px int bottom = "bottomMargin");
+ }
+
+ public final class ViewKt {
+ ctor public ViewKt();
+ method @RequiresApi(16) public static void announceForAccessibility(android.view.View, @StringRes int resource);
+ method public static void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+ method public static boolean isGone(android.view.View);
+ method public static boolean isInvisible(android.view.View);
+ method public static boolean isVisible(android.view.View);
+ method public static Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+ method public static void setGone(android.view.View, boolean value);
+ method public static void setInvisible(android.view.View, boolean value);
+ method public static void setPadding(android.view.View, @Px int size);
+ method public static void setVisible(android.view.View, boolean value);
+ method public static android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+ method public static void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+ method public static void updatePadding(android.view.View, @Px int left = "paddingLeft", @Px int top = "paddingTop", @Px int right = "paddingRight", @Px int bottom = "paddingBottom");
+ method @RequiresApi(17) public static void updatePaddingRelative(android.view.View, @Px int start = "paddingStart", @Px int top = "paddingTop", @Px int end = "paddingEnd", @Px int bottom = "paddingBottom");
+ }
+
+}
+
+package androidx.core.widget {
+
+ public final class ToastKt {
+ ctor public ToastKt();
+ method public static android.widget.Toast toast(android.content.Context, CharSequence text, int duration = "Toast.LENGTH_SHORT");
+ method public static android.widget.Toast toast(android.content.Context, @StringRes int resId, int duration = "Toast.LENGTH_SHORT");
+ }
+
+}
+
diff --git a/core/ktx/build.gradle b/core/ktx/build.gradle
new file mode 100644
index 0000000..bce0757
--- /dev/null
+++ b/core/ktx/build.gradle
@@ -0,0 +1,37 @@
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ buildTypes {
+ debug {
+ testCoverageEnabled = false // Breaks Kotlin compiler.
+ }
+ }
+}
+
+dependencies {
+ api(KOTLIN_STDLIB)
+ api(project(":annotation"))
+ api(project(":core"))
+
+ androidTestImplementation(JUNIT)
+ androidTestImplementation(TEST_RUNNER)
+ androidTestImplementation(TEST_RULES)
+ androidTestImplementation(TRUTH)
+ androidTestImplementation(project(":internal-testutils-ktx"))
+}
+
+supportLibrary {
+ name = "Core Kotlin Extensions"
+ publish = true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.CORE
+ inceptionYear = "2018"
+ description = "Kotlin extensions for 'core' artifact"
+}
diff --git a/core/ktx/src/androidTest/AndroidManifest.xml b/core/ktx/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..259b616
--- /dev/null
+++ b/core/ktx/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.core.ktx.test">
+ <application>
+ <activity android:name="androidx.core.TestActivity"/>
+ <activity android:name="androidx.core.TestPreferenceActivity"/>
+ </application>
+</manifest>
diff --git a/core/ktx/src/androidTest/assets/red.png b/core/ktx/src/androidTest/assets/red.png
new file mode 100644
index 0000000..6292f4b
--- /dev/null
+++ b/core/ktx/src/androidTest/assets/red.png
Binary files differ
diff --git a/core/ktx/src/androidTest/font_licenses.txt b/core/ktx/src/androidTest/font_licenses.txt
new file mode 100644
index 0000000..0b83a9a
--- /dev/null
+++ b/core/ktx/src/androidTest/font_licenses.txt
@@ -0,0 +1,93 @@
+Copyright 2006 The Inconsolata Project Authors
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
new file mode 100644
index 0000000..b0132ab
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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 androidx.core
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.core.ktx.test.R
+
+class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.test_activity)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt b/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt
new file mode 100644
index 0000000..43e3dee
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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 androidx.core
+
+import android.app.Activity
+import android.os.Bundle
+import android.preference.PreferenceFragment
+import androidx.core.ktx.test.R
+
+class TestPreferenceActivity : Activity() {
+
+ companion object {
+ const val TAG = "TestPreferenceActivity"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ fragmentManager.beginTransaction()
+ .add(android.R.id.content, TestPreferenceFragment(), TAG)
+ .commitNow()
+ }
+
+ class TestPreferenceFragment : PreferenceFragment() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addPreferencesFromResource(R.xml.preferences)
+ }
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt b/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt
new file mode 100644
index 0000000..de298a5
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.animation
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.support.test.InstrumentationRegistry
+import android.support.test.annotation.UiThreadTest
+import android.support.test.filters.SdkSuppress
+import android.support.test.runner.AndroidJUnit4
+import android.view.View
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AnimatorTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val view = View(context)
+
+ private lateinit var animator: Animator
+
+ @Before fun before() {
+ animator = ObjectAnimator.ofFloat(view, View.ALPHA, 0f, 1f)
+ }
+
+ @Test fun testDoOnStart() {
+ var called = false
+ animator.doOnStart {
+ called = true
+ }
+
+ animator.listeners.forEach { it.onAnimationStart(animator) }
+ assertTrue(called)
+ }
+
+ @Test fun testDoOnEnd() {
+ var called = false
+ animator.doOnEnd {
+ called = true
+ }
+
+ animator.listeners.forEach { it.onAnimationEnd(animator) }
+ assertTrue(called)
+ }
+
+ @Test fun testDoOnCancel() {
+ var cancelCalled = false
+ animator.doOnCancel {
+ cancelCalled = true
+ }
+
+ animator.listeners.forEach { it.onAnimationCancel(animator) }
+ assertTrue(cancelCalled)
+ }
+
+ @Test fun testDoOnRepeat() {
+ var called = false
+ animator.doOnRepeat {
+ called = true
+ }
+
+ animator.listeners.forEach { it.onAnimationRepeat(animator) }
+ assertTrue(called)
+ }
+
+ @UiThreadTest
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testDoOnPause() {
+ var called = false
+ animator.doOnPause {
+ called = true
+ }
+
+ // Start and pause and assert doOnPause was called
+ animator.start()
+ animator.pause()
+ assertTrue(called)
+
+ animator.cancel()
+ }
+
+ @UiThreadTest
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testDoOnResume() {
+ var called = false
+ animator.doOnResume {
+ called = true
+ }
+
+ animator.start()
+ animator.pause()
+
+ // Now resume and assert doOnResume was called
+ animator.resume()
+ assertTrue(called)
+
+ animator.cancel()
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt
new file mode 100644
index 0000000..746a634
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.content
+
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import java.util.concurrent.atomic.AtomicInteger
+
+class ContentValuesTest {
+ @Test fun valuesOfValid() {
+ val values = contentValuesOf(
+ "null" to null,
+ "string" to "string",
+ "byte" to 1.toByte(),
+ "short" to 1.toShort(),
+ "int" to 1,
+ "long" to 1L,
+ "float" to 1f,
+ "double" to 1.0,
+ "boolean" to true,
+ "byteArray" to byteArrayOf()
+ )
+ assertEquals(10, values.size())
+ assertNull(values.get("null"))
+ assertEquals("string", values.get("string"))
+ assertEquals(1.toByte(), values.get("byte"))
+ assertEquals(1.toShort(), values.get("short"))
+ assertEquals(1, values.get("int"))
+ assertEquals(1L, values.get("long"))
+ assertEquals(1f, values.get("float"))
+ assertEquals(1.0, values.get("double"))
+ assertEquals(true, values.get("boolean"))
+ assertArrayEquals(byteArrayOf(), values.get("byteArray") as ByteArray)
+ }
+
+ @Test fun valuesOfInvalid() {
+ assertThrows<IllegalArgumentException> {
+ contentValuesOf("nope" to AtomicInteger(1))
+ }.hasMessageThat().isEqualTo(
+ "Illegal value type java.util.concurrent.atomic.AtomicInteger for key \"nope\"")
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt
new file mode 100644
index 0000000..09d2355
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.content
+
+import android.content.ContextWrapper
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.ktx.test.R
+import androidx.core.getAttributeSet
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ContextTest {
+ private val context = InstrumentationRegistry.getContext()
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test fun systemService() {
+ var lookup: Class<*>? = null
+ val context = object : ContextWrapper(context) {
+ override fun getSystemServiceName(serviceClass: Class<*>): String? {
+ lookup = serviceClass
+ return if (serviceClass == Unit::class.java) "unit" else null
+ }
+
+ override fun getSystemService(name: String): Any? {
+ return if (name == "unit") Unit else null
+ }
+ }
+ val actual = context.systemService<Unit>()
+ assertEquals(Unit::class.java, lookup)
+ assertSame(Unit, actual)
+ }
+
+ @Test fun withStyledAttributes() {
+ context.withStyledAttributes(attrs = intArrayOf(android.R.attr.textColorPrimary)) {
+ val resourceId = getResourceId(0, -1)
+ assertTrue(resourceId != 1)
+ }
+
+ context.withStyledAttributes(
+ android.R.style.Theme_Light,
+ intArrayOf(android.R.attr.textColorPrimary)
+ ) {
+ val resourceId = getResourceId(0, -1)
+ assertTrue(resourceId != 1)
+ }
+
+ val attrs = context.getAttributeSet(R.layout.test_attrs)
+ context.withStyledAttributes(attrs, R.styleable.SampleAttrs) {
+ assertTrue(getInt(R.styleable.SampleAttrs_sample, -1) != -1)
+ }
+
+ context.withStyledAttributes(attrs, R.styleable.SampleAttrs, 0, 0) {
+ assertTrue(getInt(R.styleable.SampleAttrs_sample, -1) != -1)
+ }
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt
new file mode 100644
index 0000000..bf1e67d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.content
+
+import android.support.test.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class SharedPreferencesTest {
+ private val context = InstrumentationRegistry.getContext()
+
+ @Test fun editApply() {
+ val preferences = context.getSharedPreferences("prefs", 0)
+
+ preferences.edit {
+ putString("test_key1", "test_value")
+ putInt("test_key2", 100)
+ }
+
+ assertEquals("test_value", preferences.getString("test_key1", null))
+ assertEquals(100, preferences.getInt("test_key2", 0))
+ }
+
+ @Test fun editCommit() {
+ val preferences = context.getSharedPreferences("prefs", 0)
+ preferences.edit(commit = true) {
+ putString("test_key1", "test_value")
+ putInt("test_key2", 100)
+ }
+
+ assertEquals("test_value", preferences.getString("test_key1", null))
+ assertEquals(100, preferences.getInt("test_key2", 0))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt
new file mode 100644
index 0000000..fe576df
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.content.res
+
+import android.graphics.Color
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.ktx.test.R
+import androidx.testutils.assertThrows
+import androidx.core.getAttributeSet
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class TypedArrayTest {
+ private val context = InstrumentationRegistry.getContext()
+
+ @Test fun boolean() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertTrue(array.getBooleanOrThrow(R.styleable.TypedArrayTypes_boolean_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getBooleanOrThrow(R.styleable.TypedArrayTypes_boolean_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun color() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(Color.WHITE, array.getColorOrThrow(R.styleable.TypedArrayTypes_color_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getColorOrThrow(R.styleable.TypedArrayTypes_color_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun colorStateList() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+ val stateList = array.getColorStateListOrThrow(R.styleable.TypedArrayTypes_color_present)
+
+ assertEquals(Color.WHITE, stateList.defaultColor)
+
+ assertThrows<IllegalArgumentException> {
+ array.getColorStateListOrThrow(R.styleable.TypedArrayTypes_color_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun dimension() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(1f, array.getDimensionOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getDimensionOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun dimensionPixelSize() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(1,
+ array.getDimensionPixelSizeOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getDimensionPixelSizeOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun dimensionPixelOffset() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(1,
+ array.getDimensionPixelOffsetOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getDimensionPixelOffsetOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun drawable() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertNotNull(array.getDrawableOrThrow(R.styleable.TypedArrayTypes_drawable_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getDrawableOrThrow(R.styleable.TypedArrayTypes_drawable_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun float() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(0.1f, array.getFloatOrThrow(R.styleable.TypedArrayTypes_float_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getFloatOrThrow(R.styleable.TypedArrayTypes_float_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun font() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertNotNull(array.getFontOrThrow(R.styleable.TypedArrayTypes_font_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getFontOrThrow(R.styleable.TypedArrayTypes_font_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun int() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(1, array.getIntOrThrow(R.styleable.TypedArrayTypes_integer_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getIntOrThrow(R.styleable.TypedArrayTypes_integer_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun integer() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(1, array.getIntegerOrThrow(R.styleable.TypedArrayTypes_integer_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getIntegerOrThrow(R.styleable.TypedArrayTypes_integer_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test
+ fun resourceId() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals(
+ R.font.inconsolata_regular,
+ array.getResourceIdOrThrow(R.styleable.TypedArrayTypes_resource_present)
+ )
+
+ assertThrows<IllegalArgumentException> {
+ array.getResourceIdOrThrow(R.styleable.TypedArrayTypes_resource_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun string() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals("Hello", array.getStringOrThrow(R.styleable.TypedArrayTypes_string_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getStringOrThrow(R.styleable.TypedArrayTypes_string_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun text() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ assertEquals("Hello", array.getTextOrThrow(R.styleable.TypedArrayTypes_string_present))
+
+ assertThrows<IllegalArgumentException> {
+ array.getTextOrThrow(R.styleable.TypedArrayTypes_string_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun textArray() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ val text = array.getTextArrayOrThrow(R.styleable.TypedArrayTypes_text_array_present)
+ assertEquals("Hello", text[0].toString())
+ assertEquals("World", text[1].toString())
+
+ assertThrows<IllegalArgumentException> {
+ array.getTextOrThrow(R.styleable.TypedArrayTypes_text_array_absent)
+ }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+ }
+
+ @Test fun useRecyclesArray() {
+ val attrs = context.getAttributeSet(R.layout.typed_array)
+ val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+ val result = array.use {
+ it.getBoolean(R.styleable.TypedArrayTypes_boolean_present, false)
+ }
+ assertTrue(result)
+
+ assertThrows<RuntimeException> {
+ array.recycle()
+ }
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt b/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt
new file mode 100644
index 0000000..86d0731
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.database
+
+import android.database.Cursor
+import android.database.MatrixCursor
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class CursorTest {
+ @Test fun blobByName() {
+ val cursor = scalarCursor(byteArrayOf(0x01))
+ val blob = cursor.getBlob("data")
+ assertArrayEquals(byteArrayOf(0x01), blob)
+ }
+
+ @Test fun doubleByName() {
+ val cursor = scalarCursor(1.5)
+ val double = cursor.getDouble("data")
+ assertEquals(1.5, double, 0.0)
+ }
+
+ @Test fun floatByName() {
+ val cursor = scalarCursor(1.5f)
+ val float = cursor.getFloat("data")
+ assertEquals(1.5f, float, 0f)
+ }
+
+ @Test fun intByName() {
+ val cursor = scalarCursor(1)
+ val int = cursor.getInt("data")
+ assertEquals(1, int)
+ }
+
+ @Test fun longByName() {
+ val cursor = scalarCursor(1L)
+ val long = cursor.getLong("data")
+ assertEquals(1L, long)
+ }
+
+ @Test fun shortByName() {
+ val cursor = scalarCursor(1.toShort())
+ val short = cursor.getShort("data")
+ assertEquals(1.toShort(), short)
+ }
+
+ @Test fun stringByName() {
+ val cursor = scalarCursor("hey")
+ val string = cursor.getString("data")
+ assertEquals("hey", string)
+ }
+
+ @Test fun blobOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val blob = cursor.getBlobOrNull(0)
+ assertNull(blob)
+ }
+
+ @Test fun doubleOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val double = cursor.getDoubleOrNull(0)
+ assertNull(double)
+ }
+
+ @Test fun floatOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val float = cursor.getFloatOrNull(0)
+ assertNull(float)
+ }
+
+ @Test fun intOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val int = cursor.getIntOrNull(0)
+ assertNull(int)
+ }
+
+ @Test fun longOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val long = cursor.getLongOrNull(0)
+ assertNull(long)
+ }
+
+ @Test fun shortOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val short = cursor.getShortOrNull(0)
+ assertNull(short)
+ }
+
+ @Test fun stringOrNullByIndex() {
+ val cursor = scalarCursor(null)
+ val string = cursor.getStringOrNull(0)
+ assertNull(string)
+ }
+
+ @Test fun blobOrNullByName() {
+ val cursor = scalarCursor(null)
+ val blob = cursor.getBlobOrNull("data")
+ assertNull(blob)
+ }
+
+ @Test fun doubleOrNullByName() {
+ val cursor = scalarCursor(null)
+ val double = cursor.getDoubleOrNull("data")
+ assertNull(double)
+ }
+
+ @Test fun floatOrNullByName() {
+ val cursor = scalarCursor(null)
+ val float = cursor.getFloatOrNull("data")
+ assertNull(float)
+ }
+
+ @Test fun intOrNullByName() {
+ val cursor = scalarCursor(null)
+ val int = cursor.getIntOrNull("data")
+ assertNull(int)
+ }
+
+ @Test fun longOrNullByName() {
+ val cursor = scalarCursor(null)
+ val long = cursor.getLongOrNull("data")
+ assertNull(long)
+ }
+
+ @Test fun shortOrNullByName() {
+ val cursor = scalarCursor(null)
+ val short = cursor.getShortOrNull("data")
+ assertNull(short)
+ }
+
+ @Test fun stringOrNullByName() {
+ val cursor = scalarCursor(null)
+ val string = cursor.getStringOrNull("data")
+ assertNull(string)
+ }
+
+ private fun scalarCursor(item: Any?): Cursor = MatrixCursor(arrayOf("data")).apply {
+ addRow(arrayOf(item))
+ moveToFirst() // Prepare for consumers to read.
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt b/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt
new file mode 100644
index 0000000..c8ed67d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.database.sqlite
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+import android.support.test.InstrumentationRegistry
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class SQLiteDatabaseTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val openHelper = object : SQLiteOpenHelper(context, null, null, 1) {
+ override fun onCreate(db: SQLiteDatabase) {
+ db.execSQL("CREATE TABLE test(name TEXT)")
+ }
+
+ override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
+ }
+ }
+ private val db = openHelper.writableDatabase
+
+ @Test fun throwingBodyNotSuccessful() {
+ val exception = RuntimeException()
+ assertThrows<RuntimeException> {
+ db.transaction {
+ insert("test", null, ContentValues().apply { put("name", "Alice") })
+ throw exception
+ }
+ }.isSameAs(exception)
+
+ val query = db.rawQuery("SELECT COUNT(*) FROM test", emptyArray())
+ query.moveToFirst()
+ assertEquals(0L, query.getLong(0))
+ query.close()
+ }
+
+ @Test fun bodyReturnValue() {
+ val result = db.transaction {
+ "Hey"
+ }
+ assertEquals("Hey", result)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/extensions.kt b/core/ktx/src/androidTest/java/androidx/core/extensions.kt
new file mode 100644
index 0000000..dc07142
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/extensions.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 androidx.core
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.util.Xml
+import androidx.annotation.LayoutRes
+import org.xmlpull.v1.XmlPullParser
+
+@SuppressLint("ResourceType")
+fun Context.getAttributeSet(@LayoutRes layoutId: Int): AttributeSet {
+ val parser = resources.getXml(layoutId)
+ var type = parser.next()
+ while (type != XmlPullParser.START_TAG) {
+ type = parser.next()
+ }
+ return Xml.asAttributeSet(parser)
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt
new file mode 100644
index 0000000..25b318b
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BitmapTest {
+ @Test fun create() {
+ val bitmap = createBitmap(7, 9)
+ assertEquals(7, bitmap.width)
+ assertEquals(9, bitmap.height)
+ assertEquals(Bitmap.Config.ARGB_8888, bitmap.config)
+ }
+
+ @Test fun createWithConfig() {
+ val bitmap = createBitmap(7, 9, config = Bitmap.Config.RGB_565)
+ assertEquals(Bitmap.Config.RGB_565, bitmap.config)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun createWithColorSpace() {
+ val colorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB)
+ val bitmap = createBitmap(7, 9, colorSpace = colorSpace)
+ assertEquals(colorSpace, bitmap.colorSpace)
+ }
+
+ @Test fun scale() {
+ val b = createBitmap(7, 9).scale(3, 5)
+ assertEquals(3, b.width)
+ assertEquals(5, b.height)
+ }
+
+ @Test fun applyCanvas() {
+ val p = createBitmap(2, 2).applyCanvas {
+ drawColor(0x40302010)
+ }.getPixel(1, 1)
+
+ assertEquals(0x40302010, p)
+ }
+
+ @Test fun getPixel() {
+ val b = createBitmap(2, 2).applyCanvas {
+ drawColor(0x40302010)
+ }
+ assertEquals(0x40302010, b[1, 1])
+ }
+
+ @Test fun setPixel() {
+ val b = createBitmap(2, 2)
+ b[1, 1] = 0x40302010
+ assertEquals(0x40302010, b[1, 1])
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt
new file mode 100644
index 0000000..e940aa9
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Matrix
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class CanvasTest {
+ private val values = FloatArray(9)
+ private val canvas = Canvas(createBitmap(1, 1))
+
+ @Suppress("DEPRECATION")
+ @Test fun withSave() {
+ val beforeCount = canvas.saveCount
+
+ canvas.matrix.getValues(values)
+ val x = values[Matrix.MTRANS_X]
+ val y = values[Matrix.MTRANS_Y]
+
+ canvas.withSave {
+ assertThat(beforeCount).isLessThan(saveCount)
+ translate(10.0f, 10.0f)
+ }
+
+ canvas.matrix.getValues(values)
+ assertEquals(x, values[Matrix.MTRANS_X])
+ assertEquals(y, values[Matrix.MTRANS_Y])
+
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+
+ @Test fun withTranslation() {
+ val beforeCount = canvas.saveCount
+ canvas.withTranslation(x = 16.0f, y = 32.0f) {
+ assertThat(beforeCount).isLessThan(saveCount)
+
+ @Suppress("DEPRECATION")
+ matrix.getValues(values) // will work for a software canvas
+
+ assertEquals(16.0f, values[Matrix.MTRANS_X])
+ assertEquals(32.0f, values[Matrix.MTRANS_Y])
+ }
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+
+ @Test fun withRotation() {
+ val beforeCount = canvas.saveCount
+ canvas.withRotation(degrees = 90.0f, pivotX = 16.0f, pivotY = 32.0f) {
+ assertThat(beforeCount).isLessThan(saveCount)
+
+ @Suppress("DEPRECATION")
+ matrix.getValues(values) // will work for a software canvas
+
+ assertEquals(48.0f, values[Matrix.MTRANS_X])
+ assertEquals(16.0f, values[Matrix.MTRANS_Y])
+ assertEquals(-1.0f, values[Matrix.MSKEW_X])
+ assertEquals(1.0f, values[Matrix.MSKEW_Y])
+ }
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+
+ @Test fun withScale() {
+ val beforeCount = canvas.saveCount
+ canvas.withScale(x = 2.0f, y = 4.0f, pivotX = 16.0f, pivotY = 32.0f) {
+ assertThat(beforeCount).isLessThan(saveCount)
+
+ @Suppress("DEPRECATION")
+ matrix.getValues(values) // will work for a software canvas
+
+ assertEquals(-16.0f, values[Matrix.MTRANS_X])
+ assertEquals(-96.0f, values[Matrix.MTRANS_Y])
+ assertEquals(2.0f, values[Matrix.MSCALE_X])
+ assertEquals(4.0f, values[Matrix.MSCALE_Y])
+ }
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+
+ @Test fun withSkew() {
+ val beforeCount = canvas.saveCount
+ canvas.withSkew(x = 2.0f, y = 4.0f) {
+ assertThat(beforeCount).isLessThan(saveCount)
+
+ @Suppress("DEPRECATION")
+ matrix.getValues(values) // will work for a software canvas
+
+ assertEquals(2.0f, values[Matrix.MSKEW_X])
+ assertEquals(4.0f, values[Matrix.MSKEW_Y])
+ }
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+
+ @Suppress("DEPRECATION")
+ @Test fun withMatrix() {
+ val originMatrix = canvas.matrix
+
+ val inputMatrix = Matrix()
+ inputMatrix.postTranslate(16.0f, 32.0f)
+ inputMatrix.postRotate(90.0f, 16.0f, 32.0f)
+ inputMatrix.postScale(2.0f, 4.0f, 16.0f, 32.0f)
+
+ val beforeCount = canvas.saveCount
+ canvas.withMatrix(inputMatrix) {
+ assertThat(beforeCount).isLessThan(saveCount)
+ assertEquals(inputMatrix, matrix)
+ }
+
+ assertEquals(originMatrix, canvas.matrix)
+ assertEquals(beforeCount, canvas.saveCount)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt
new file mode 100644
index 0000000..5c647d1
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ColorTest {
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun destructuringColor() {
+ val (r, g, b, a) = 0x337f3010.toColor()
+ assertEquals(0.5f, r, 1e-2f)
+ assertEquals(0.19f, g, 1e-2f)
+ assertEquals(0.06f, b, 1e-2f)
+ assertEquals(0.2f, a, 1e-2f)
+ }
+
+ @Test fun destructuringInt() {
+ val (a, r, g, b) = 0x337f3010
+ assertEquals(0x33, a)
+ assertEquals(0x7f, r)
+ assertEquals(0x30, g)
+ assertEquals(0x10, b)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun intToColor() = assertEquals(Color.valueOf(0x337f3010), 0x337f3010.toColor())
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun intToColorLong() = assertEquals(Color.pack(0x337f3010), 0x337f3010.toColorLong())
+
+ @Test fun alpha() = assertEquals(0x33, 0x337f3010.alpha)
+ @Test fun red() = assertEquals(0x7f, 0x337f3010.red)
+ @Test fun green() = assertEquals(0x30, 0x337f3010.green)
+ @Test fun blue() = assertEquals(0x10, 0x337f3010.blue)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun luminance() = assertEquals(0.212f, 0xff7f7f7f.toInt().luminance, 1e-3f)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun longToColor() {
+ assertEquals(Color.valueOf(0x337f3010), Color.pack(0x337f3010).toColor())
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun longToColorInt() = assertEquals(0x337f3010, Color.pack(0x337f3010).toColorInt())
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun destructuringLong() {
+ val (r, g, b, a) = Color.pack(0x337f3010)
+ assertEquals(0.20f, a, 1e-2f)
+ assertEquals(0.50f, r, 1e-2f)
+ assertEquals(0.19f, g, 1e-2f)
+ assertEquals(0.06f, b, 1e-2f)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun alphaLong() = assertEquals(0.20f, Color.pack(0x337f3010).alpha, 1e-2f)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun redLong() = assertEquals(0.50f, Color.pack(0x337f3010).red, 1e-2f)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun greenLong() = assertEquals(0.19f, Color.pack(0x337f3010).green, 1e-2f)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun blueLong() = assertEquals(0.06f, Color.pack(0x337f3010).blue, 1e-2f)
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun luminanceLong() {
+ assertEquals(0.212f, Color.pack(0xff7f7f7f.toInt()).luminance, 1e-3f)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun isSrgb() {
+ assertTrue(0x337f3010.toColorLong().isSrgb)
+ val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, ColorSpace.get(ColorSpace.Named.BT2020))
+ assertFalse(c.isSrgb)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun isWideGamut() {
+ assertFalse(0x337f3010.toColorLong().isWideGamut)
+ val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, ColorSpace.get(ColorSpace.Named.BT2020))
+ assertTrue(c.isWideGamut)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun getColorSpace() {
+ val sRGB = ColorSpace.get(ColorSpace.Named.SRGB)
+ assertEquals(sRGB, 0x337f3010.toColorLong().colorSpace)
+
+ val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020)
+ val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, bt2020)
+ assertEquals(bt2020, c.colorSpace)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun addColorsSameColorSpace() {
+ val (r, g, b, a) = 0x7f7f0000.toColor() + 0x7f007f00.toColor()
+ assertEquals(0.16f, r, 1e-2f)
+ assertEquals(0.33f, g, 1e-2f)
+ assertEquals(0.00f, b, 1e-2f)
+ assertEquals(0.75f, a, 1e-2f)
+ }
+
+ @Test fun stringToColorInt() = assertEquals(Color.GREEN, "#00ff00".toColorInt())
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt
new file mode 100644
index 0000000..61fce4f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class MatrixTest {
+ @Test fun translationMatrix() {
+ val r = translationMatrix(2.0f, 3.0f).values()
+ assertEquals(1.0f, r[Matrix.MSCALE_X])
+ assertEquals(0.0f, r[Matrix.MSKEW_X])
+ assertEquals(2.0f, r[Matrix.MTRANS_X])
+ assertEquals(0.0f, r[Matrix.MSKEW_Y])
+ assertEquals(1.0f, r[Matrix.MSCALE_Y])
+ assertEquals(3.0f, r[Matrix.MTRANS_Y])
+ }
+
+ @Test fun scaleMatrix() {
+ val r = scaleMatrix(2.0f, 3.0f).values()
+ assertEquals(2.0f, r[Matrix.MSCALE_X])
+ assertEquals(0.0f, r[Matrix.MSKEW_X])
+ assertEquals(0.0f, r[Matrix.MTRANS_X])
+ assertEquals(0.0f, r[Matrix.MSKEW_Y])
+ assertEquals(3.0f, r[Matrix.MSCALE_Y])
+ assertEquals(0.0f, r[Matrix.MTRANS_Y])
+ }
+
+ @Test fun rotationMatrix() {
+ val r = rotationMatrix(90.0f, 2.0f, 3.0f).values()
+ assertEquals(0.0f, r[Matrix.MSCALE_X])
+ assertEquals(-1.0f, r[Matrix.MSKEW_X])
+ assertEquals(5.0f, r[Matrix.MTRANS_X])
+ assertEquals(1.0f, r[Matrix.MSKEW_Y])
+ assertEquals(0.0f, r[Matrix.MSCALE_Y])
+ assertEquals(1.0f, r[Matrix.MTRANS_Y])
+ }
+
+ @Test fun multiply() {
+ val t = translationMatrix(2.0f, 3.0f)
+ val s = scaleMatrix(2.0f, 3.0f)
+ val r = (s * t).values()
+
+ assertEquals(4.0f, r[Matrix.MTRANS_X], 1e-4f)
+ assertEquals(9.0f, r[Matrix.MTRANS_Y], 1e-4f)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
new file mode 100644
index 0000000..36f2cba
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics
+
+import android.graphics.Path
+import android.graphics.RectF
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class PathTest {
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun testFlatten() {
+ val p = Path()
+
+ // Start with several moves
+ p.moveTo(5.0f, 5.0f)
+ p.moveTo(10.0f, 10.0f)
+ p.lineTo(20.0f, 20.0f)
+ p.lineTo(30.0f, 10.0f)
+ // Several moves in the middle
+ p.moveTo(40.0f, 10.0f)
+ p.moveTo(50.0f, 10.0f)
+ p.lineTo(60.0f, 20.0f)
+ // End with several moves
+ p.moveTo(10.0f, 10.0f)
+ p.moveTo(30.0f, 30.0f)
+
+ var count = 0
+ p.flatten().forEach {
+ count++
+ assertNotEquals(it.startFraction, it.endFraction)
+ }
+ assertEquals(3, count)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testUnion() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+ val p = r1 + r2
+ val r = RectF()
+ p.computeBounds(r, true)
+
+ assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testAnd() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+ val p = r1 and r2
+ val r = RectF()
+ p.computeBounds(r, true)
+
+ assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testDifference() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+ val p = r1 - r2
+ val r = RectF()
+ p.computeBounds(r, true)
+
+ assertEquals(RectF(0.0f, 0.0f, 5.0f, 10.0f), r)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testIntersection() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+ val p = r1 or r2
+ val r = RectF()
+ p.computeBounds(r, true)
+
+ assertEquals(RectF(5.0f, 0.0f, 10.0f, 10.0f), r)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testEmptyIntersection() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 2.0f, 2.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 5.0f, 7.0f, 7.0f, Path.Direction.CW) }
+
+ val p = r1 or r2
+ assertTrue(p.isEmpty)
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test fun testXor() {
+ val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+ val r2 = Path().apply { addRect(5.0f, 5.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+ val p = r1 xor r2
+ val r = RectF()
+ p.computeBounds(r, true)
+
+ assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt
new file mode 100644
index 0000000..6d0029f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics
+
+import android.graphics.Color
+import android.graphics.Picture
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PictureTest {
+ @Test fun record() {
+ val p = Picture().record(1, 1) {
+ drawColor(Color.RED)
+ }
+ val v = createBitmap(1, 1).applyCanvas {
+ drawPicture(p)
+ }.getPixel(0, 0)
+ assertEquals(0xffff0000.toInt(), v)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt
new file mode 100644
index 0000000..791d35f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.PointF
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PointTest {
+ @Test fun destructuringInt() {
+ val (x, y) = Point(2, 3)
+ assertEquals(2, x)
+ assertEquals(3, y)
+ }
+
+ @Test fun destructuringFloat() {
+ val (x, y) = PointF(2.0f, 3.0f)
+ assertEquals(2.0f, x)
+ assertEquals(3.0f, y)
+ }
+
+ @Test fun offsetInt() {
+ val (x, y) = Point(2, 3) + 2
+ assertEquals(4, x)
+ assertEquals(5, y)
+ }
+
+ @Test fun offsetFloat() {
+ val (x, y) = PointF(2.0f, 3.0f) + 2.0f
+ assertEquals(4.0f, x)
+ assertEquals(5.0f, y)
+ }
+
+ @Test fun offsetPoint() {
+ val (x, y) = Point(2, 3) + Point(1, 2)
+ assertEquals(3, x)
+ assertEquals(5, y)
+ }
+
+ @Test fun offsetPointF() {
+ val (x, y) = PointF(2.0f, 3.0f) + PointF(1.0f, 2.0f)
+ assertEquals(3.0f, x)
+ assertEquals(5.0f, y)
+ }
+
+ @Test fun negativeOffsetInt() {
+ val (x, y) = Point(2, 3) - 2
+ assertEquals(0, x)
+ assertEquals(1, y)
+ }
+
+ @Test fun negativeOffsetFloat() {
+ val (x, y) = PointF(2.0f, 3.0f) - 2.0f
+ assertEquals(0.0f, x)
+ assertEquals(1.0f, y)
+ }
+
+ @Test fun negativeOffsetPoint() {
+ val (x, y) = Point(2, 3) - Point(1, 2)
+ assertEquals(1, x)
+ assertEquals(1, y)
+ }
+
+ @Test fun negativeOffsetPointF() {
+ val (x, y) = PointF(2.0f, 3.0f) - PointF(1.0f, 2.0f)
+ assertEquals(1.0f, x)
+ assertEquals(1.0f, y)
+ }
+
+ @Test fun negateInt() {
+ val (x, y) = -Point(2, 3)
+ assertEquals(-2, x)
+ assertEquals(-3, y)
+ }
+
+ @Test fun negateFloat() {
+ val (x, y) = -PointF(2.0f, 3.0f)
+ assertEquals(-2.0f, x)
+ assertEquals(-3.0f, y)
+ }
+
+ @Test fun toPointF() {
+ val pointF = Point(1, 2).toPointF()
+ assertEquals(1f, pointF.x, 0f)
+ assertEquals(2f, pointF.y, 0f)
+ }
+
+ @Test fun toPoint() {
+ assertEquals(Point(1, 2), PointF(1.1f, 2.8f).toPoint())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt
new file mode 100644
index 0000000..9b44a50
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics
+
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PorterDuffTest {
+ @Test fun xfermode() {
+ val p = createBitmap(1, 1).applyCanvas {
+ val p = Paint().apply { color = 0xffffffff.toInt() }
+ drawRect(0f, 0f, 1f, 1f, p)
+
+ p.color = 0x7f00ff00
+ p.xfermode = PorterDuff.Mode.SRC.toXfermode()
+ drawRect(0f, 0f, 1f, 1f, p)
+ }.getPixel(0, 0)
+
+ assertEquals(0x7f00ff00, p)
+ }
+
+ @Test fun colorFilter() {
+ val p = createBitmap(1, 1).applyCanvas {
+ val p = Paint().apply { color = 0xffffffff.toInt() }
+ drawRect(0f, 0f, 1f, 1f, p)
+
+ p.color = 0xff000000.toInt()
+ p.colorFilter = PorterDuff.Mode.SRC.toColorFilter(0xff00ff00.toInt())
+ drawRect(0f, 0f, 1f, 1f, p)
+ }.getPixel(0, 0)
+
+ assertEquals(0xff00ff00.toInt(), p)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt
new file mode 100644
index 0000000..8b2613f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class RectTest {
+ @Test fun destructuringInt() {
+ val (l, t, r, b) = Rect(4, 8, 16, 24)
+ assertEquals(4, l)
+ assertEquals(8, t)
+ assertEquals(16, r)
+ assertEquals(24, b)
+ }
+
+ @Test fun destructuringFloat() {
+ val (l, t, r, b) = RectF(4.0f, 8.0f, 16.0f, 24.0f)
+ assertEquals(4.0f, l)
+ assertEquals(8.0f, t)
+ assertEquals(16.0f, r)
+ assertEquals(24.0f, b)
+ }
+
+ @Test fun unionInt() {
+ val (l, t, r, b) = Rect(0, 0, 4, 4) + Rect(-1, -1, 6, 6)
+ assertEquals(-1, l)
+ assertEquals(-1, t)
+ assertEquals(6, r)
+ assertEquals(6, b)
+ }
+
+ @Test fun unionAsAndInt() {
+ val (l, t, r, b) = Rect(0, 0, 4, 4) and Rect(-1, -1, 6, 6)
+ assertEquals(-1, l)
+ assertEquals(-1, t)
+ assertEquals(6, r)
+ assertEquals(6, b)
+ }
+
+ @Test fun unionFloat() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) + RectF(-1.0f, -1.0f, 6.0f, 6.0f)
+ assertEquals(-1.0f, l)
+ assertEquals(-1.0f, t)
+ assertEquals(6.0f, r)
+ assertEquals(6.0f, b)
+ }
+
+ @Test fun unionAsAndFloat() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) and RectF(-1.0f, -1.0f, 6.0f, 6.0f)
+ assertEquals(-1.0f, l)
+ assertEquals(-1.0f, t)
+ assertEquals(6.0f, r)
+ assertEquals(6.0f, b)
+ }
+
+ @Test fun differenceInt() {
+ val r = Rect(0, 0, 4, 4) - Rect(-1, 2, 6, 6)
+ assertEquals(Rect(0, 0, 4, 2), r.bounds)
+ }
+
+ @Test fun differenceFloat() {
+ val r = RectF(0.0f, 0.0f, 4.0f, 4.0f) - RectF(-1.0f, 2.0f, 6.0f, 6.0f)
+ assertEquals(Rect(0, 0, 4, 2), r.bounds)
+ }
+
+ @Test fun intersectionAsOrInt() {
+ val (l, t, r, b) = Rect(0, 0, 4, 4) or Rect(2, 2, 6, 6)
+ assertEquals(2, l)
+ assertEquals(2, t)
+ assertEquals(4, r)
+ assertEquals(4, b)
+ }
+
+ @Test fun intersectionAsOrFloat() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) or RectF(2.0f, 2.0f, 6.0f, 6.0f)
+ assertEquals(2.0f, l)
+ assertEquals(2.0f, t)
+ assertEquals(4.0f, r)
+ assertEquals(4.0f, b)
+ }
+
+ @Test fun xorInt() {
+ val r = Rect(0, 0, 4, 4) xor Rect(2, 2, 6, 6)
+ assertEquals(Rect(0, 0, 4, 4) and Rect(2, 2, 6, 6), r.bounds)
+ assertFalse(r.contains(3, 3))
+ }
+
+ @Test fun xorFloat() {
+ val r = RectF(0.0f, 0.0f, 4.0f, 4.0f) xor RectF(2.0f, 2.0f, 6.0f, 6.0f)
+ assertEquals(Rect(0, 0, 4, 4) and Rect(2, 2, 6, 6), r.bounds)
+ assertFalse(r.contains(3, 3))
+ }
+
+ @Test fun offsetInt() {
+ val (l, t, r, b) = Rect(0, 0, 2, 2) + 2
+ assertEquals(l, 2)
+ assertEquals(t, 2)
+ assertEquals(r, 4)
+ assertEquals(b, 4)
+ }
+
+ @Test fun offsetPoint() {
+ val (l, t, r, b) = Rect(0, 0, 2, 2) + Point(1, 2)
+ assertEquals(l, 1)
+ assertEquals(t, 2)
+ assertEquals(r, 3)
+ assertEquals(b, 4)
+ }
+
+ @Test fun offsetFloat() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) + 2.0f
+ assertEquals(l, 2.0f)
+ assertEquals(t, 2.0f)
+ assertEquals(r, 4.0f)
+ assertEquals(b, 4.0f)
+ }
+
+ @Test fun offsetPointF() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) + PointF(1.0f, 2.0f)
+ assertEquals(l, 1.0f)
+ assertEquals(t, 2.0f)
+ assertEquals(r, 3.0f)
+ assertEquals(b, 4.0f)
+ }
+
+ @Test fun negativeOffsetInt() {
+ val (l, t, r, b) = Rect(0, 0, 2, 2) - 2
+ assertEquals(l, -2)
+ assertEquals(t, -2)
+ assertEquals(r, 0)
+ assertEquals(b, 0)
+ }
+
+ @Test fun negativeOffsetPoint() {
+ val (l, t, r, b) = Rect(0, 0, 2, 2) - Point(1, 2)
+ assertEquals(l, -1)
+ assertEquals(t, -2)
+ assertEquals(r, 1)
+ assertEquals(b, 0)
+ }
+
+ @Test fun negativeOffsetFloat() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) - 2.0f
+ assertEquals(l, -2.0f)
+ assertEquals(t, -2.0f)
+ assertEquals(r, 0.0f)
+ assertEquals(b, 0.0f)
+ }
+
+ @Test fun negativeOffsetPointF() {
+ val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) - PointF(1.0f, 2.0f)
+ assertEquals(l, -1.0f)
+ assertEquals(t, -2.0f)
+ assertEquals(r, 1.0f)
+ assertEquals(b, 0.0f)
+ }
+
+ @Test fun pointInside() {
+ assertTrue(Point(1, 1) in Rect(0, 0, 2, 2))
+ assertTrue(PointF(1.0f, 1.0f) in RectF(0.0f, 0.0f, 2.0f, 2.0f))
+ }
+
+ @Test fun toRect() {
+ assertEquals(
+ Rect(0, 1, 2, 3),
+ RectF(0f, 1f, 2f, 3f).toRect())
+
+ assertEquals(
+ Rect(0, 1, 2, 3),
+ RectF(0.1f, 1.1f, 1.9f, 2.9f).toRect())
+ }
+
+ @Test fun toRectF() {
+ val rectF = Rect(0, 1, 2, 3).toRectF()
+ assertEquals(0f, rectF.left, 0f)
+ assertEquals(1f, rectF.top, 0f)
+ assertEquals(2f, rectF.right, 0f)
+ assertEquals(3f, rectF.bottom, 0f)
+ }
+
+ @Test fun toRegionInt() {
+ assertEquals(Rect(1, 1, 5, 5), Rect(1, 1, 5, 5).toRegion().bounds)
+ }
+
+ @Test fun toRegionFloat() {
+ assertEquals(Rect(1, 1, 5, 5), RectF(1.1f, 1.1f, 4.8f, 4.8f).toRegion().bounds)
+ }
+
+ @Test fun transformRectToRect() {
+ val m = Matrix()
+ m.setScale(2.0f, 2.0f)
+
+ val r = RectF(2.0f, 2.0f, 5.0f, 7.0f).transform(m)
+ assertEquals(4f, r.left, 0f)
+ assertEquals(4f, r.top, 0f)
+ assertEquals(10f, r.right, 0f)
+ assertEquals(14f, r.bottom, 0f)
+ }
+
+ @Test fun transformRectNotPreserved() {
+ val m = Matrix()
+ m.setRotate(45.0f)
+
+ val (l, t, r, b) = RectF(-1.0f, -1.0f, 1.0f, 1.0f).transform(m)
+ assertEquals(-1.414f, l, 1e-3f)
+ assertEquals(-1.414f, t, 1e-3f)
+ assertEquals( 1.414f, r, 1e-3f)
+ assertEquals( 1.414f, b, 1e-3f)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt
new file mode 100644
index 0000000..21e6443
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.Region
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class RegionTest {
+ @Test fun containsPoint() {
+ assertFalse(Point(1, 1) in Region())
+ assertTrue(Point(1, 1) in Region(0, 0, 2, 2))
+ }
+
+ @Test fun unionRect() {
+ val r = Region(0, 0, 2, 2) + Rect(4, 4, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertTrue(Point(5, 5) in r)
+ }
+
+ @Test fun unionRegion() {
+ val r = Region(0, 0, 2, 2) + Region(4, 4, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertTrue(Point(5, 5) in r)
+ }
+
+ @Test fun unionAsAndRect() {
+ val r = Region(0, 0, 2, 2) and Rect(4, 4, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertTrue(Point(5, 5) in r)
+ }
+
+ @Test fun unionAsAndRegion() {
+ val r = Region(0, 0, 2, 2) and Region(4, 4, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertTrue(Point(5, 5) in r)
+ }
+
+ @Test fun differenceRect() {
+ val r = Region(0, 0, 4, 4) - Rect(2, 2, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertFalse(Point(5, 5) in r)
+ }
+
+ @Test fun differenceRegion() {
+ val r = Region(0, 0, 4, 4) - Region(2, 2, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ assertFalse(Point(5, 5) in r)
+ }
+
+ @Test fun unaryMinus() {
+ val r = Rect(0, 0, 10, 10) - Rect(4, 4, 6, 6)
+ assertTrue(Point(1, 1) in r)
+ assertFalse(Point(5, 5) in r)
+
+ val i = -r
+ assertFalse(Point(1, 1) in i)
+ assertTrue(Point(5, 5) in i)
+ }
+
+ @Test fun not() {
+ val r = Rect(0, 0, 10, 10) - Rect(4, 4, 6, 6)
+ assertTrue(Point(1, 1) in r)
+ assertFalse(Point(5, 5) in r)
+
+ val i = !r
+ assertFalse(Point(1, 1) in i)
+ assertTrue(Point(5, 5) in i)
+ }
+
+ @Test fun orRect() {
+ val r = Region(0, 0, 4, 4) or Rect(2, 2, 6, 6)
+ assertFalse(Point(1, 1) in r)
+ assertTrue(Point(3, 3) in r)
+ }
+
+ @Test fun orRegion() {
+ val r = Region(0, 0, 4, 4) or Region(2, 2, 6, 6)
+ assertFalse(Point(1, 1) in r)
+ assertTrue(Point(3, 3) in r)
+ }
+
+ @Test fun xorRect() {
+ val r = Region(0, 0, 4, 4) xor Rect(2, 2, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ }
+
+ @Test fun xorRegion() {
+ val r = Region(0, 0, 4, 4) xor Region(2, 2, 6, 6)
+ assertFalse(Point(3, 3) in r)
+ assertTrue(Point(1, 1) in r)
+ }
+
+ @Test fun iteratorForLoop() {
+ val region = Region(0, 0, 4, 4) -
+ Rect(2, 2, 6, 6)
+ var count = 0
+ var r = Rect()
+ for (rect in region) {
+ count++
+ assertNotSame(r, rect)
+ r = rect
+ }
+ assertEquals(2, count)
+ }
+
+ @Test fun iteratorOutOfBounds() {
+ val region = Region(0, 0, 4, 4) -
+ Rect(2, 2, 6, 6)
+ val it = region.iterator()
+ it.next()
+ it.next()
+ assertThrows<IndexOutOfBoundsException> {
+ it.next()
+ }
+ }
+
+ @Test fun iteratorForEach() {
+ val region = Region(0, 0, 4, 4) -
+ Rect(2, 2, 6, 6)
+ var count = 0
+ var r = Rect()
+ region.forEach {
+ count++
+ assertNotSame(r, it)
+ r = it
+ }
+ assertEquals(2, count)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt
new file mode 100644
index 0000000..ad2b63c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Shader
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ShaderTest {
+ @Test
+ fun testTransform() {
+ @Suppress("DEPRECATION")
+ val shader = Shader()
+ val values = FloatArray(9)
+ val matrix = Matrix()
+
+ shader.transform {
+ setTranslate(10f, 30f)
+ }
+
+ // Now read matrix from Shader
+ shader.getLocalMatrix(matrix)
+ matrix.getValues(values)
+
+ // Assert that the values are as expected
+ assertEquals(10f, values[Matrix.MTRANS_X])
+ assertEquals(30f, values[Matrix.MTRANS_Y])
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt
new file mode 100644
index 0000000..4c4ceb6
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics.drawable
+
+import android.support.test.InstrumentationRegistry
+import androidx.core.graphics.createBitmap
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BitmapDrawableTest {
+ private val context = InstrumentationRegistry.getContext()
+
+ @Test fun fromBitmap() {
+ val b = createBitmap(1, 1)
+ val drawable = b.toDrawable(context.resources)
+ assertEquals(b, drawable.bitmap)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt
new file mode 100644
index 0000000..ad7a39b
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics.drawable
+
+import android.graphics.Color
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ColorDrawableTest {
+ @Test fun fromInt() {
+ val drawable = Color.CYAN.toDrawable()
+ assertEquals(Color.CYAN, drawable.color)
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test fun fromColor() {
+ val drawable = Color.valueOf(Color.CYAN).toDrawable()
+ assertEquals(Color.CYAN, drawable.color)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt
new file mode 100644
index 0000000..6455d0a
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics.drawable
+
+import android.graphics.Bitmap.Config
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.support.test.InstrumentationRegistry
+import androidx.core.graphics.createBitmap
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class DrawableTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val resources = context.resources
+
+ @Test fun bitmapDrawableNoSizeNoConfig() {
+ val original = createBitmap(10, 10).apply {
+ eraseColor(Color.RED)
+ }
+ val drawable = BitmapDrawable(resources, original)
+
+ val bitmap = drawable.toBitmap()
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(5, 5))
+ }
+
+ @Test fun bitmapDrawableNoSizeRetainedConfig() {
+ val original = createBitmap(10, 10).apply {
+ eraseColor(Color.RED)
+ }
+ val drawable = BitmapDrawable(resources, original)
+
+ val bitmap = drawable.toBitmap(config = Config.ARGB_8888)
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(5, 5))
+ }
+
+ @Test fun bitmapDrawableNoSizeDifferentConfig() {
+ val original = createBitmap(10, 10).apply {
+ eraseColor(Color.RED)
+ }
+ val drawable = BitmapDrawable(resources, original)
+
+ val bitmap = drawable.toBitmap(config = Config.ARGB_8888)
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(5, 5))
+ }
+
+ @Test fun bitmapDrawableDifferentSizeNoConfig() {
+ val original = createBitmap(10, 10).apply {
+ eraseColor(Color.RED)
+ }
+ val drawable = BitmapDrawable(resources, original)
+
+ val bitmap = drawable.toBitmap(20, 20)
+ assertEquals(20, bitmap.width)
+ assertEquals(20, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(10, 10))
+ }
+
+ @Test fun bitmapDrawableDifferentSizeDifferentConfig() {
+ val original = createBitmap(10, 10).apply {
+ eraseColor(Color.RED)
+ }
+ val drawable = BitmapDrawable(resources, original)
+
+ val bitmap = drawable.toBitmap(20, 20, Config.ARGB_8888)
+ assertEquals(20, bitmap.width)
+ assertEquals(20, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(10, 10))
+ }
+
+ @Test fun drawableNoConfig() {
+ val drawable = object : ColorDrawable(Color.RED) {
+ override fun getIntrinsicWidth() = 10
+ override fun getIntrinsicHeight() = 10
+ }
+
+ val bitmap = drawable.toBitmap()
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(5, 5))
+ }
+
+ @Test fun drawableConfig() {
+ val drawable = object : ColorDrawable(Color.RED) {
+ override fun getIntrinsicWidth() = 10
+ override fun getIntrinsicHeight() = 10
+ }
+
+ val bitmap = drawable.toBitmap(config = Config.RGB_565)
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+ assertEquals(Config.RGB_565, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(5, 5))
+ }
+
+ @Test fun drawableSize() {
+ val drawable = object : ColorDrawable(Color.RED) {
+ override fun getIntrinsicWidth() = 10
+ override fun getIntrinsicHeight() = 10
+ }
+
+ val bitmap = drawable.toBitmap(20, 20)
+ assertEquals(20, bitmap.width)
+ assertEquals(20, bitmap.height)
+ assertEquals(Config.ARGB_8888, bitmap.config)
+ assertEquals(Color.RED, bitmap.getPixel(10, 10))
+ }
+
+ @Test fun oldBoundsRestored() {
+ val drawable = object : ColorDrawable(Color.RED) {
+ override fun getIntrinsicWidth() = 10
+ override fun getIntrinsicHeight() = 10
+ }
+ drawable.setBounds(2, 2, 8, 8)
+
+ val bitmap = drawable.toBitmap()
+ assertEquals(10, bitmap.width)
+ assertEquals(10, bitmap.height)
+
+ assertEquals(2, drawable.bounds.left)
+ assertEquals(2, drawable.bounds.top)
+ assertEquals(8, drawable.bounds.right)
+ assertEquals(8, drawable.bounds.bottom)
+ }
+
+ @Test fun updateBoundsTest() {
+ val drawable = object : ColorDrawable(Color.RED) {
+ override fun getIntrinsicWidth() = 10
+ override fun getIntrinsicHeight() = 10
+ }
+ drawable.setBounds(0, 0, 10, 10)
+
+ drawable.updateBounds(1, 2, 3, 4)
+
+ assertEquals(1, drawable.bounds.left)
+ assertEquals(2, drawable.bounds.top)
+ assertEquals(3, drawable.bounds.right)
+ assertEquals(4, drawable.bounds.bottom)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt
new file mode 100644
index 0000000..e2dd84c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.ARGB_8888
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.graphics.createBitmap
+import androidx.core.net.toUri
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.io.File
+
+@SdkSuppress(minSdkVersion = 26)
+class IconTest {
+ private val context = InstrumentationRegistry.getContext()
+
+ @Test fun fromBitmapAdaptive() {
+ val density = context.resources.displayMetrics.density
+
+ val edge = (108.0f * density + 0.5f).toInt()
+ val bitmap = Bitmap.createBitmap(edge, edge, ARGB_8888).apply {
+ eraseColor(Color.RED)
+ }
+ val icon = bitmap.toAdaptiveIcon()
+
+ val rendered = icon.toIntrinsicBitmap()
+ val masked = (72.0f * density + 0.5f).toInt()
+ assertEquals(masked, rendered.width)
+ assertEquals(masked, rendered.height)
+ // Grab a pixel from the middle to ensure we are not being masked.
+ assertEquals(Color.RED, rendered.getPixel(masked / 2, masked / 2))
+ }
+
+ @Test fun fromBitmap() {
+ val bitmap = createBitmap(1, 1).apply {
+ eraseColor(Color.RED)
+ }
+ val icon = bitmap.toIcon()
+
+ val rendered = icon.toIntrinsicBitmap()
+ assertEquals(1, rendered.width)
+ assertEquals(1, rendered.height)
+ assertEquals(Color.RED, rendered.getPixel(0, 0))
+ }
+
+ @Test fun fromUri() {
+ // Icon can't read from file:///android_asset/red.png so copy to a real file.
+ val cacheFile = File(context.cacheDir, "red.png")
+ context.assets.open("red.png").use { cacheFile.writeBytes(it.readBytes()) }
+
+ val uri = cacheFile.toUri()
+ val icon = uri.toIcon()
+
+ val rendered = icon.toIntrinsicBitmap()
+ assertEquals(1, rendered.width)
+ assertEquals(1, rendered.height)
+ assertEquals(Color.RED, rendered.getPixel(0, 0))
+ }
+
+ @Test fun fromByteArray() {
+ val bytes = context.assets.open("red.png").use { it.readBytes() }
+ val icon = bytes.toIcon()
+
+ val rendered = icon.toIntrinsicBitmap()
+ assertEquals(1, rendered.width)
+ assertEquals(1, rendered.height)
+ assertEquals(Color.RED, rendered.getPixel(0, 0))
+ }
+
+ private fun Icon.toIntrinsicBitmap(): Bitmap {
+ val drawable = loadDrawable(context)
+ val bitmap = createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight)
+ drawable.setBounds(0, 0, drawable.intrinsicHeight, drawable.intrinsicHeight)
+ drawable.draw(Canvas(bitmap))
+ return bitmap
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt b/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt
new file mode 100644
index 0000000..d25de35
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.net
+
+import android.net.Uri
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.io.File
+
+class UriTest {
+ @Test fun uriFromString() {
+ val string = "https://test.example.com/foo?bar#baz"
+ assertEquals(Uri.parse(string), string.toUri())
+ }
+
+ @Test fun uriFromFile() {
+ val file = File("/path/to/my/file")
+ assertEquals(Uri.fromFile(file), file.toUri())
+ }
+
+ @Test fun fileFromUri() {
+ val uri = Uri.parse("path/to/my/file")
+ assertEquals(File(uri.path), uri.toFile())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt
new file mode 100644
index 0000000..924e9ad
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.os
+
+import android.graphics.Rect
+import android.os.Binder
+import android.os.Bundle
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.util.Size
+import android.util.SizeF
+import android.view.View
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertSame
+import org.junit.Test
+import java.util.concurrent.atomic.AtomicInteger
+
+class BundleTest {
+ @Test fun bundleOfValid() {
+ val bundleValue = Bundle()
+ val charSequenceValue = "hey"
+ val parcelableValue = Rect(1, 2, 3, 4)
+ val serializableValue = AtomicInteger(1)
+
+ val bundle = bundleOf(
+ "null" to null,
+
+ "boolean" to true,
+ "byte" to 1.toByte(),
+ "char" to 'a',
+ "double" to 1.0,
+ "float" to 1f,
+ "int" to 1,
+ "long" to 1L,
+ "short" to 1.toShort(),
+
+ "bundle" to bundleValue,
+ "charSequence" to charSequenceValue,
+ "parcelable" to parcelableValue,
+
+ "booleanArray" to booleanArrayOf(),
+ "byteArray" to byteArrayOf(),
+ "charArray" to charArrayOf(),
+ "doubleArray" to doubleArrayOf(),
+ "floatArray" to floatArrayOf(),
+ "intArray" to intArrayOf(),
+ "longArray" to longArrayOf(),
+ "shortArray" to shortArrayOf(),
+
+ "parcelableArray" to arrayOf(parcelableValue),
+ "stringArray" to arrayOf("hey"),
+ "charSequenceArray" to arrayOf<CharSequence>("hey"),
+ "serializableArray" to arrayOf(serializableValue),
+
+ "serializable" to serializableValue
+ )
+
+ assertEquals(25, bundle.size())
+
+ assertNull(bundle["null"])
+
+ assertEquals(true, bundle["boolean"])
+ assertEquals(1.toByte(), bundle["byte"])
+ assertEquals('a', bundle["char"])
+ assertEquals(1.0, bundle["double"])
+ assertEquals(1f, bundle["float"])
+ assertEquals(1, bundle["int"])
+ assertEquals(1L, bundle["long"])
+ assertEquals(1.toShort(), bundle["short"])
+
+ assertSame(bundleValue, bundle["bundle"])
+ assertSame(charSequenceValue, bundle["charSequence"])
+ assertSame(parcelableValue, bundle["parcelable"])
+
+ assertArrayEquals(booleanArrayOf(), bundle["booleanArray"] as BooleanArray)
+ assertArrayEquals(byteArrayOf(), bundle["byteArray"] as ByteArray)
+ assertArrayEquals(charArrayOf(), bundle["charArray"] as CharArray)
+ assertArrayEquals(doubleArrayOf(), bundle["doubleArray"] as DoubleArray, 0.0)
+ assertArrayEquals(floatArrayOf(), bundle["floatArray"] as FloatArray, 0f)
+ assertArrayEquals(intArrayOf(), bundle["intArray"] as IntArray)
+ assertArrayEquals(longArrayOf(), bundle["longArray"] as LongArray)
+ assertArrayEquals(shortArrayOf(), bundle["shortArray"] as ShortArray)
+
+ assertThat(bundle["parcelableArray"] as Array<*>).asList().containsExactly(parcelableValue)
+ assertThat(bundle["stringArray"] as Array<*>).asList().containsExactly("hey")
+ assertThat(bundle["charSequenceArray"] as Array<*>).asList().containsExactly("hey")
+ assertThat(bundle["serializableArray"] as Array<*>).asList()
+ .containsExactly(serializableValue)
+
+ assertSame(serializableValue, bundle["serializable"])
+ }
+
+ @SdkSuppress(minSdkVersion = 18)
+ @Test fun bundleOfValidApi18() {
+ val binderValue = Binder()
+ val bundle = bundleOf("binder" to binderValue)
+ assertSame(binderValue, bundle["binder"])
+ }
+
+ @SdkSuppress(minSdkVersion = 21)
+ @Test fun bundleOfValidApi21() {
+ val sizeValue = Size(1, 1)
+ val sizeFValue = SizeF(1f, 1f)
+
+ val bundle = bundleOf(
+ "size" to sizeValue,
+ "sizeF" to sizeFValue
+ )
+
+ assertSame(sizeValue, bundle["size"])
+ assertSame(sizeFValue, bundle["sizeF"])
+ }
+
+ @Test fun bundleOfInvalid() {
+ assertThrows<IllegalArgumentException> {
+ bundleOf("nope" to View(InstrumentationRegistry.getContext()))
+ }.hasMessageThat().isEqualTo("Illegal value type android.view.View for key \"nope\"")
+
+ assertThrows<IllegalArgumentException> {
+ bundleOf("nopes" to arrayOf(View(InstrumentationRegistry.getContext())))
+ }.hasMessageThat().isEqualTo("Illegal value array type android.view.View for key \"nopes\"")
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt
new file mode 100644
index 0000000..e0fdf4e
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.os
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.SystemClock
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeUnit.MILLISECONDS
+import java.util.concurrent.TimeUnit.SECONDS
+
+class HandlerTest {
+ private val handlerThread = HandlerThread("handler-test")
+ private lateinit var handler: Handler
+
+ @Before fun before() {
+ handlerThread.start()
+ handler = Handler(handlerThread.looper)
+ }
+
+ @After fun after() {
+ handlerThread.quit()
+ }
+
+ @Test fun postDelayedLambdaMillis() {
+ var called = 0
+ handler.postDelayed(10) {
+ called++
+ }
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(1, called)
+ }
+
+ @Test fun postDelayedLambdaMillisRemoved() {
+ var called = 0
+ val runnable = handler.postDelayed(10) {
+ called++
+ }
+ handler.removeCallbacks(runnable)
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(0, called)
+ }
+
+ @Test fun postAtTimeLambda() {
+ var called = 0
+ handler.postAtTime(SystemClock.uptimeMillis() + 10) {
+ called++
+ }
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(1, called)
+ }
+
+ @Test fun postAtTimeLambdaRemoved() {
+ var called = 0
+ val runnable = handler.postAtTime(SystemClock.uptimeMillis() + 10) {
+ called++
+ }
+ handler.removeCallbacks(runnable)
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(0, called)
+ }
+
+ @Test fun postAtTimeLambdaWithTokenRuns() {
+ val token = Any()
+ var called = 0
+ handler.postAtTime(SystemClock.uptimeMillis() + 10, token) {
+ called++
+ }
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(1, called)
+ }
+
+ @Test fun postAtTimeLambdaWithTokenCancelWithToken() {
+ // This test uses the token to cancel the runnable as it's the only way we have to verify
+ // that the Runnable was actually posted with the token.
+
+ val token = Any()
+ var called = 0
+ handler.postAtTime(SystemClock.uptimeMillis() + 10, token) {
+ called++
+ }
+ handler.removeCallbacksAndMessages(token)
+
+ handler.await(20, MILLISECONDS)
+ assertEquals(0, called)
+ }
+
+ private fun Handler.await(amount: Long, unit: TimeUnit) {
+ val latch = CountDownLatch(1)
+ postDelayed(latch::countDown, unit.toMillis(amount))
+
+ // Wait up to 1s longer than desired to account for time skew.
+ val wait = unit.toMillis(amount) + SECONDS.toMillis(1)
+ assertTrue(latch.await(wait, MILLISECONDS))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt
new file mode 100644
index 0000000..8683155
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.os
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.view.View
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class PersistableBundleTest {
+ @Test fun persistableBundleOfValid() {
+ val bundle = persistableBundleOf(
+ "null" to null,
+
+ "double" to 1.0,
+ "int" to 1,
+ "long" to 1L,
+
+ "string" to "hey",
+
+ "doubleArray" to doubleArrayOf(),
+ "intArray" to intArrayOf(),
+ "longArray" to longArrayOf(),
+
+ "stringArray" to arrayOf("hey")
+ )
+
+ assertEquals(9, bundle.size())
+
+ assertNull(bundle["null"])
+
+ assertEquals(1.0, bundle["double"])
+ assertEquals(1, bundle["int"])
+ assertEquals(1L, bundle["long"])
+
+ assertEquals("hey", bundle["string"])
+
+ assertArrayEquals(doubleArrayOf(), bundle["doubleArray"] as DoubleArray, 0.0)
+ assertArrayEquals(intArrayOf(), bundle["intArray"] as IntArray)
+ assertArrayEquals(longArrayOf(), bundle["longArray"] as LongArray)
+
+ assertThat(bundle["stringArray"] as Array<*>).asList().containsExactly("hey")
+ }
+
+ @SdkSuppress(minSdkVersion = 22)
+ @Test fun persistableBundleOfValidApi22() {
+ val bundle = persistableBundleOf(
+ "boolean" to true,
+ "booleanArray" to booleanArrayOf()
+ )
+
+ assertEquals(true, bundle["boolean"])
+ assertArrayEquals(booleanArrayOf(), bundle["booleanArray"] as BooleanArray)
+ }
+
+ @Test fun persistableBundleOfInvalid() {
+ assertThrows<IllegalArgumentException> {
+ persistableBundleOf("nope" to View(InstrumentationRegistry.getContext()))
+ }.hasMessageThat().isEqualTo("Illegal value type android.view.View for key \"nope\"")
+
+ assertThrows<IllegalArgumentException> {
+ persistableBundleOf("nopes" to arrayOf(View(InstrumentationRegistry.getContext())))
+ }.hasMessageThat().isEqualTo("Illegal value array type android.view.View for key \"nopes\"")
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt b/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt
new file mode 100644
index 0000000..0653a28
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.preference
+
+import android.preference.Preference
+import android.preference.PreferenceFragment
+import android.preference.PreferenceGroup
+import android.support.test.InstrumentationRegistry
+import android.support.test.rule.ActivityTestRule
+import androidx.core.TestPreferenceActivity
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class PreferenceGroupTest {
+
+ @JvmField
+ @Rule
+ val rule = ActivityTestRule(TestPreferenceActivity::class.java)
+ private val context = InstrumentationRegistry.getContext()
+ private lateinit var preferenceGroup: PreferenceGroup
+
+ @Before fun setup() {
+ preferenceGroup = (rule
+ .activity
+ .fragmentManager
+ .findFragmentByTag(TestPreferenceActivity.TAG) as PreferenceFragment).preferenceScreen
+ }
+
+ @Test fun get() {
+ val key = "key"
+ val preference = Preference(context)
+ preference.key = key
+ preferenceGroup.addPreference(preference)
+ assertSame(preference, preferenceGroup[key])
+ assertSame(preference, preferenceGroup[0])
+ }
+
+ @Test fun contains() {
+ val preference = Preference(context)
+ assertFalse(preference in preferenceGroup)
+ assertTrue(preference !in preferenceGroup)
+ preferenceGroup.addPreference(preference)
+ assertFalse(preference !in preferenceGroup)
+ assertTrue(preference in preferenceGroup)
+ preferenceGroup.removePreference(preference)
+ assertFalse(preference in preferenceGroup)
+ assertTrue(preference !in preferenceGroup)
+ }
+
+ @Test fun plusAssign() {
+ assertEquals(0, preferenceGroup.preferenceCount)
+
+ val preference1 = Preference(context)
+ preferenceGroup += preference1
+ assertEquals(1, preferenceGroup.preferenceCount)
+ assertSame(preference1, preferenceGroup.getPreference(0))
+
+ val preference2 = Preference(context)
+ preferenceGroup += preference2
+ assertEquals(2, preferenceGroup.preferenceCount)
+ assertSame(preference2, preferenceGroup.getPreference(1))
+ }
+
+ @Test fun minusAssign() {
+ val preference1 = Preference(context)
+ preferenceGroup.addPreference(preference1)
+ val preference2 = Preference(context)
+ preferenceGroup.addPreference(preference2)
+
+ assertEquals(2, preferenceGroup.preferenceCount)
+
+ preferenceGroup -= preference2
+ assertEquals(1, preferenceGroup.preferenceCount)
+ assertSame(preference1, preferenceGroup.getPreference(0))
+
+ preferenceGroup -= preference1
+ assertEquals(0, preferenceGroup.preferenceCount)
+ }
+
+ @Test fun size() {
+ assertEquals(0, preferenceGroup.size)
+
+ val preference = Preference(context)
+ preferenceGroup.addPreference(preference)
+ assertEquals(1, preferenceGroup.size)
+
+ preferenceGroup.removePreference(preference)
+ assertEquals(0, preferenceGroup.size)
+ }
+
+ @Test fun isEmpty() {
+ assertTrue(preferenceGroup.isEmpty())
+ preferenceGroup.addPreference(Preference(context))
+ assertFalse(preferenceGroup.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ assertFalse(preferenceGroup.isNotEmpty())
+ preferenceGroup.addPreference(Preference(context))
+ assertTrue(preferenceGroup.isNotEmpty())
+ }
+
+ @Test fun forEach() {
+ preferenceGroup.forEach {
+ fail("Empty preference group should not invoke lambda")
+ }
+
+ val preference1 = Preference(context).apply { key = "ASD" }
+ preferenceGroup.addPreference(preference1)
+ val preference2 = Preference(context)
+ preferenceGroup.addPreference(preference2)
+
+ val preferences = mutableListOf<Preference>()
+ preferenceGroup.forEach {
+ preferences += it
+ }
+ assertThat(preferences).containsExactly(preference1, preference2)
+ }
+
+ @Test fun forEachIndexed() {
+ preferenceGroup.forEach {
+ fail("Empty preference group should not invoke lambda")
+ }
+
+ val preference1 = Preference(context)
+ preferenceGroup.addPreference(preference1)
+ val preference2 = Preference(context)
+ preferenceGroup.addPreference(preference2)
+
+ val preferences = mutableListOf<Preference>()
+ preferenceGroup.forEachIndexed { index, preference ->
+ assertEquals(index, preferences.size)
+ preferences += preference
+ }
+ assertThat(preferences).containsExactly(preference1, preference2)
+ }
+
+ @Test fun iterator() {
+ val preference1 = Preference(context)
+ preferenceGroup.addPreference(preference1)
+ val preference2 = Preference(context)
+ preferenceGroup.addPreference(preference2)
+
+ val iterator = preferenceGroup.iterator()
+ assertTrue(iterator.hasNext())
+ assertSame(preference1, iterator.next())
+ assertTrue(iterator.hasNext())
+ assertSame(preference2, iterator.next())
+ assertFalse(iterator.hasNext())
+ assertThrows<IndexOutOfBoundsException> {
+ iterator.next()
+ }
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt
new file mode 100644
index 0000000..0ce0a3c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class CharSequenceTest {
+ @Test fun isDigitsOnly() {
+ assertTrue("012345".isDigitsOnly())
+ assertFalse("0123 abc".isDigitsOnly())
+ }
+
+ @Test fun trimmedLength() {
+ assertEquals(6, "string ".trimmedLength())
+ assertEquals(6, " string".trimmedLength())
+ assertEquals(6, "string".trimmedLength())
+ assertEquals(0, "".trimmedLength())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt
new file mode 100644
index 0000000..0fef5ed
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import android.text.Html.FROM_HTML_MODE_COMPACT
+import android.text.Html.ImageGetter
+import android.text.Html.TagHandler
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class HtmlTest {
+ private val imageGetter = ImageGetter { null }
+ private val tagHandler = TagHandler { _, _, _, _ -> }
+
+ @Test fun parseAsHtml() {
+ val parsed = "<b>Hi</b> © > <".parseAsHtml().toString()
+ assertEquals("Hi \u00a9 > <", parsed)
+ }
+
+ @Test fun parseAsHtmlFlags() {
+ val parsed = "<b>Hi</b> © > <".parseAsHtml(FROM_HTML_MODE_COMPACT).toString()
+ assertEquals("Hi \u00a9 > <", parsed)
+ }
+
+ @Test fun parseAsHtmlImageGetterTagHandler() {
+ val parsed = "<b>Hi</b> © > <"
+ .parseAsHtml(FROM_HTML_MODE_COMPACT, imageGetter, tagHandler)
+ .toString()
+ assertEquals("Hi \u00a9 > <", parsed)
+ }
+
+ @Test fun parseAsHtmlFlagsImageGetterTagHandler() {
+ val parsed = "<b>Hi</b> © > <"
+ .parseAsHtml(imageGetter = imageGetter, tagHandler = tagHandler)
+ .toString()
+ assertEquals("Hi \u00a9 > <", parsed)
+ }
+
+ @Test fun convertToHtml() {
+ val html = buildSpannedString {
+ bold {
+ append("Hi")
+ }
+ }.toHtml()
+ assertTrue(html, html.contains("<b>Hi</b>"))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt
new file mode 100644
index 0000000..5d67e83
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.text
+
+import android.graphics.Color.RED
+import android.graphics.Color.YELLOW
+import android.graphics.Typeface.BOLD
+import android.graphics.Typeface.ITALIC
+import android.text.SpannedString
+import android.text.style.BackgroundColorSpan
+import android.text.style.BulletSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.RelativeSizeSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.SubscriptSpan
+import android.text.style.SuperscriptSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class SpannableStringBuilderTest {
+ @Test fun builder() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello,")
+ append(" World")
+ }
+ assertEquals("Hello, World", result.toString())
+ }
+
+ @Test fun builderInSpan() {
+ val bold = StyleSpan(BOLD)
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ inSpans(bold) {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val boldSpan = spans.filterIsInstance<StyleSpan>().single()
+ assertSame(bold, boldSpan)
+ assertEquals(7, result.getSpanStart(bold))
+ assertEquals(12, result.getSpanEnd(bold))
+ }
+
+ @Test fun builderBold() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ bold {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val bold = spans.filterIsInstance<StyleSpan>().single()
+ assertEquals(BOLD, bold.style)
+ assertEquals(7, result.getSpanStart(bold))
+ assertEquals(12, result.getSpanEnd(bold))
+ }
+
+ @Test fun builderItalic() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ italic {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val italic = spans.filterIsInstance<StyleSpan>().single()
+ assertEquals(ITALIC, italic.style)
+ assertEquals(7, result.getSpanStart(italic))
+ assertEquals(12, result.getSpanEnd(italic))
+ }
+
+ @Test fun builderUnderline() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ underline {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val underline = spans.filterIsInstance<UnderlineSpan>().single()
+ assertEquals(7, result.getSpanStart(underline))
+ assertEquals(12, result.getSpanEnd(underline))
+ }
+
+ @Test fun builderColor() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ color(RED) {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val color = spans.filterIsInstance<ForegroundColorSpan>().single()
+ assertEquals(RED, color.foregroundColor)
+ assertEquals(7, result.getSpanStart(color))
+ assertEquals(12, result.getSpanEnd(color))
+ }
+
+ @Test fun builderBackgroundColor() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ backgroundColor(RED) {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val color = spans.filterIsInstance<BackgroundColorSpan>().single()
+ assertEquals(RED, color.backgroundColor)
+ assertEquals(7, result.getSpanStart(color))
+ assertEquals(12, result.getSpanEnd(color))
+ }
+
+ @Test fun builderStrikeThrough() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ strikeThrough {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val strikeThrough = spans.filterIsInstance<StrikethroughSpan>().single()
+ assertEquals(7, result.getSpanStart(strikeThrough))
+ assertEquals(12, result.getSpanEnd(strikeThrough))
+ }
+
+ @Test fun builderScale() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ scale(2f) {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(1, spans.size)
+
+ val scale = spans.filterIsInstance<RelativeSizeSpan>().single()
+ assertEquals(2f, scale.sizeChange)
+ assertEquals(7, result.getSpanStart(scale))
+ assertEquals(12, result.getSpanEnd(scale))
+ }
+
+ @Test fun nested() {
+ val result: SpannedString = buildSpannedString {
+ color(RED) {
+ append('H')
+ inSpans(SubscriptSpan()) {
+ append('e')
+ }
+ append('l')
+ inSpans(SuperscriptSpan()) {
+ append('l')
+ }
+ append('o')
+ }
+ append(", ")
+ backgroundColor(YELLOW) {
+ append('W')
+ underline {
+ append('o')
+ bold {
+ append('r')
+ }
+ append('l')
+ }
+ append('d')
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(6, spans.size)
+
+ val color = spans.filterIsInstance<ForegroundColorSpan>().single()
+ assertEquals(RED, color.foregroundColor)
+ assertEquals(0, result.getSpanStart(color))
+ assertEquals(5, result.getSpanEnd(color))
+
+ val subscript = spans.filterIsInstance<SubscriptSpan>().single()
+ assertEquals(1, result.getSpanStart(subscript))
+ assertEquals(2, result.getSpanEnd(subscript))
+
+ val superscript = spans.filterIsInstance<SuperscriptSpan>().single()
+ assertEquals(3, result.getSpanStart(superscript))
+ assertEquals(4, result.getSpanEnd(superscript))
+
+ val backgroundColor = spans.filterIsInstance<BackgroundColorSpan>().single()
+ assertEquals(YELLOW, backgroundColor.backgroundColor)
+ assertEquals(7, result.getSpanStart(backgroundColor))
+ assertEquals(12, result.getSpanEnd(backgroundColor))
+
+ val underline = spans.filterIsInstance<UnderlineSpan>().single()
+ assertEquals(8, result.getSpanStart(underline))
+ assertEquals(11, result.getSpanEnd(underline))
+
+ val bold = spans.filterIsInstance<StyleSpan>().single()
+ assertEquals(BOLD, bold.style)
+ assertEquals(9, result.getSpanStart(bold))
+ assertEquals(10, result.getSpanEnd(bold))
+ }
+
+ @Test fun multipleSpans() {
+ val result: SpannedString = buildSpannedString {
+ append("Hello, ")
+ inSpans(BulletSpan(), UnderlineSpan()) {
+ append("World")
+ }
+ }
+ assertEquals("Hello, World", result.toString())
+
+ val spans = result.getSpans<Any>()
+ assertEquals(2, spans.size)
+
+ val bullet = spans.filterIsInstance<BulletSpan>().single()
+ assertEquals(7, result.getSpanStart(bullet))
+ assertEquals(12, result.getSpanEnd(bullet))
+ val underline = spans.filterIsInstance<UnderlineSpan>().single()
+ assertEquals(7, result.getSpanStart(underline))
+ assertEquals(12, result.getSpanEnd(underline))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt
new file mode 100644
index 0000000..5fe3c5d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import android.graphics.Typeface.BOLD
+import android.text.SpannableString
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SpannableStringTest {
+
+ @Test fun toSpannableString() = assertTrue("Hello, World".toSpannable() is SpannableString)
+
+ @Test fun plusAssign() {
+ val s = "Hello, World".toSpannable()
+
+ val bold = StyleSpan(BOLD)
+ s += bold
+ assertEquals(0, s.getSpanStart(bold))
+ assertEquals(s.length, s.getSpanEnd(bold))
+ }
+
+ @Test fun minusAssign() {
+ val s = "Hello, World".toSpannable()
+ val bold = StyleSpan(BOLD)
+ s += bold
+ assertTrue(s.getSpans<Any>().isNotEmpty())
+ s -= bold
+ assertTrue(s.getSpans<Any>().isEmpty())
+ }
+
+ @Test fun clearSpans() {
+ val s = "Hello, World".toSpannable()
+ s += StyleSpan(BOLD)
+ s += UnderlineSpan()
+ assertTrue(s.getSpans<Any>().isNotEmpty())
+ s.clearSpans()
+ assertTrue(s.getSpans<Any>().isEmpty())
+ }
+
+ @Test fun setIndices() {
+ val s = "Hello, World".toSpannable()
+ s[0, 5] = StyleSpan(BOLD)
+ s[7, 12] = UnderlineSpan()
+
+ val spans = s.getSpans<Any>()
+
+ val bold = spans.filterIsInstance<StyleSpan>().single()
+ assertEquals(0, s.getSpanStart(bold))
+ assertEquals(5, s.getSpanEnd(bold))
+
+ val underline = spans.filterIsInstance<UnderlineSpan>().single()
+ assertEquals(7, s.getSpanStart(underline))
+ assertEquals(12, s.getSpanEnd(underline))
+ }
+
+ @Test fun setRange() {
+ val s = "Hello, World".toSpannable()
+ s[0..5] = StyleSpan(BOLD)
+ s[7..12] = UnderlineSpan()
+
+ val spans = s.getSpans<Any>()
+
+ val bold = spans.filterIsInstance<StyleSpan>().single()
+ assertEquals(0, s.getSpanStart(bold))
+ assertEquals(5, s.getSpanEnd(bold))
+
+ val underline = spans.filterIsInstance<UnderlineSpan>().single()
+ assertEquals(7, s.getSpanStart(underline))
+ assertEquals(12, s.getSpanEnd(underline))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt
new file mode 100644
index 0000000..cb8930a
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import android.graphics.Typeface.BOLD
+import android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+import android.text.SpannedString
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SpannedStringTest {
+
+ @Test fun toSpanned() = assertTrue("Hello, World".toSpanned() is SpannedString)
+
+ @Test fun getSpans() {
+ val bold = StyleSpan(BOLD)
+ val underline = UnderlineSpan()
+
+ val s = "Hello, World".toSpannable()
+ s.setSpan(bold, 0, 5, SPAN_INCLUSIVE_EXCLUSIVE)
+ s.setSpan(underline, 7, 12, SPAN_INCLUSIVE_EXCLUSIVE)
+
+ assertSame(bold, s.getSpans<StyleSpan>().single())
+ assertSame(underline, s.getSpans<UnderlineSpan>().single())
+ assertEquals(s.getSpans<Any>().size, 2)
+
+ assertSame(bold, s.getSpans<Any>(0, 6).single())
+ assertSame(underline, s.getSpans<Any>(6, 12).single())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
new file mode 100644
index 0000000..2812799
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class StringTest {
+ @Test fun htmlEncode() {
+ assertEquals("<> & " '", """<> & " '""".htmlEncode())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt b/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt
new file mode 100644
index 0000000..c3a2d22
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.transition
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.support.test.rule.ActivityTestRule
+import android.transition.Fade
+import android.transition.Transition
+import android.transition.TransitionManager
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.core.TestActivity
+import androidx.core.ktx.test.R
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+
+@SdkSuppress(minSdkVersion = 19)
+class TransitionTest {
+ @JvmField @Rule val rule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+
+ private lateinit var transition: Transition
+
+ @Before fun setup() {
+ transition = Fade().setDuration(50)
+ }
+
+ @Test fun testDoOnStart() {
+ val called = AtomicBoolean()
+ transition.doOnStart {
+ called.set(true)
+ }
+
+ startTransition(transition)
+ assertTrue(called.get())
+ }
+
+ @Test fun testDoOnEnd() {
+ val called = AtomicBoolean()
+ transition.doOnEnd {
+ called.set(true)
+ }
+
+ val latch = CountDownLatch(1)
+ transition.addListener(object : Transition.TransitionListener {
+ override fun onTransitionEnd(transition: Transition?) {
+ latch.countDown()
+ }
+
+ override fun onTransitionResume(transition: Transition?) = Unit
+ override fun onTransitionPause(transition: Transition?) = Unit
+ override fun onTransitionCancel(transition: Transition?) = Unit
+ override fun onTransitionStart(transition: Transition?) = Unit
+ })
+
+ startTransition(transition)
+
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ assertTrue(called.get())
+ }
+
+ private fun startTransition(t: Transition) {
+ rule.runOnUiThread {
+ val sceneRoot = rule.activity.findViewById<ViewGroup>(R.id.root)
+ val view = rule.activity.findViewById<ImageView>(R.id.image_view)
+
+ TransitionManager.beginDelayedTransition(sceneRoot, t)
+
+ view.visibility = View.INVISIBLE
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt
new file mode 100644
index 0000000..5eddef2
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 19)
+class ArrayMapTest {
+ @Test fun empty() {
+ val map = arrayMapOf<String, String>()
+ assertEquals(0, map.size)
+ }
+
+ @Test fun nonEmpty() {
+ val map = arrayMapOf("foo" to "bar", "bar" to "baz")
+ assertThat(map).containsExactly("foo", "bar", "bar", "baz")
+ }
+
+ @Test fun duplicateKeyKeepsLast() {
+ val map = arrayMapOf("foo" to "bar", "foo" to "baz")
+ assertThat(map).containsExactly("foo", "baz")
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt
new file mode 100644
index 0000000..934f18c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 23)
+class ArraySetTest {
+ @Test fun empty() {
+ val set = arraySetOf<String>()
+ assertEquals(0, set.size)
+ }
+
+ @Test fun nonEmpty() {
+ val set = arraySetOf("foo", "bar", "baz")
+ assertThat(set).containsExactly("foo", "bar", "baz")
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt
new file mode 100644
index 0000000..59fc8d9
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.AtomicFile
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import java.io.IOException
+
+@SdkSuppress(minSdkVersion = 17)
+class AtomicFileTest {
+ @get:Rule val temporaryFolder = TemporaryFolder()
+
+ private lateinit var file: AtomicFile
+
+ @Before fun before() {
+ file = AtomicFile(temporaryFolder.newFile())
+ }
+
+ @Test fun tryWriteSuccess() {
+ file.tryWrite {
+ it.write(byteArrayOf(0, 1, 2))
+ }
+ val bytes = file.openRead().use { it.readBytes() }
+ assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+ }
+
+ @Test fun tryWriteFail() {
+ val os = file.startWrite()
+ os.write(byteArrayOf(0, 1, 2))
+ file.finishWrite(os)
+
+ val failure = IOException("Broken!")
+ assertThrows<IOException> {
+ file.tryWrite {
+ it.write(byteArrayOf(3, 4, 5))
+ throw failure
+ }
+ }.isSameAs(failure)
+
+ val bytes = file.openRead().use { it.readBytes() }
+ assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+ }
+
+ @Test fun writeBytes() {
+ file.writeBytes(byteArrayOf(0, 1, 2))
+
+ val bytes = file.openRead().use { it.readBytes() }
+ assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+ }
+
+ @Test fun writeText() {
+ file.writeText("Hey")
+
+ val bytes = file.openRead().use { it.readBytes() }
+ assertArrayEquals(byteArrayOf(72, 101, 121), bytes)
+ }
+
+ @Test fun writeTextCharset() {
+ file.writeText("Hey", charset = Charsets.UTF_16LE)
+
+ val bytes = file.openRead().use { it.readBytes() }
+ assertArrayEquals(byteArrayOf(72, 0, 101, 0, 121, 0), bytes)
+ }
+
+ @Test fun readBytes() {
+ val os = file.startWrite()
+ os.write(byteArrayOf(0, 1, 2))
+ file.finishWrite(os)
+
+ assertArrayEquals(byteArrayOf(0, 1, 2), file.readBytes())
+ }
+
+ @Test fun readText() {
+ val os = file.startWrite()
+ os.write(byteArrayOf(72, 101, 121))
+ file.finishWrite(os)
+
+ assertEquals("Hey", file.readText())
+ }
+
+ @Test fun readTextCharset() {
+ val os = file.startWrite()
+ os.write(byteArrayOf(72, 0, 101, 0, 121, 0))
+ file.finishWrite(os)
+
+ assertEquals("Hey", file.readText(charset = Charsets.UTF_16LE))
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt
new file mode 100644
index 0000000..b79e064
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Half
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 26)
+class HalfTest {
+ @Test fun shortToHalf() = assertEquals(Half(1.toShort()), 1.toShort().toHalf())
+
+ @Test fun floatToHalf() = assertEquals(Half(1f), 1f.toHalf())
+
+ @Test fun doubleToHalf() = assertEquals(Half(1.0), 1.0.toHalf())
+
+ @Test fun stringToHalf() = assertEquals(Half("1"), "1".toHalf())
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt
new file mode 100644
index 0000000..0a345f0
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import android.view.View
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.util.Locale
+
+class LocaleTest {
+ @Test fun layoutDirectionWithLTR() {
+ val ltrLocale = Locale.Builder().setLanguage("en").build()
+ assertEquals(View.LAYOUT_DIRECTION_LTR, ltrLocale.layoutDirection)
+ }
+
+ @Test fun layoutDirectionWithRTL() {
+ val rtlLocale = Locale.Builder().setLanguage("ar").build()
+ assertEquals(View.LAYOUT_DIRECTION_RTL, rtlLocale.layoutDirection)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt
new file mode 100644
index 0000000..c2289e6
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.LongSparseArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 16)
+class LongSparseArrayTest {
+ @Test fun sizeProperty() {
+ val array = LongSparseArray<String>()
+ assertEquals(0, array.size)
+ array.put(1L, "one")
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = LongSparseArray<String>()
+ assertFalse(1L in array)
+ array.put(1L, "one")
+ assertTrue(1L in array)
+ }
+
+ @Test fun containsOperatorWithValue() {
+ val array = LongSparseArray<String>()
+
+ array.put(1L, "one")
+ assertFalse(2L in array)
+
+ array.put(2L, "two")
+ assertTrue(2L in array)
+ }
+
+ @Test fun setOperator() {
+ val array = LongSparseArray<String>()
+ array[1L] = "one"
+ assertEquals("one", array.get(1L))
+ }
+
+ @Test fun plusOperator() {
+ val first = LongSparseArray<String>().apply { put(1L, "one") }
+ val second = LongSparseArray<String>().apply { put(2L, "two") }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1L, combined.keyAt(0))
+ assertEquals("one", combined.valueAt(0))
+ assertEquals(2L, combined.keyAt(1))
+ assertEquals("two", combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.containsKey(1L))
+ array.put(1L, "one")
+ assertTrue(array.containsKey(1L))
+ }
+
+ @Test fun containsKeyWithValue() {
+ val array = LongSparseArray<String>()
+
+ array.put(1L, "one")
+ assertFalse(array.containsKey(2L))
+
+ array.put(2L, "one")
+ assertTrue(array.containsKey(2L))
+ }
+
+ @Test fun containsValue() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.containsValue("one"))
+ array.put(1L, "one")
+ assertTrue(array.containsValue("one"))
+ }
+
+ @Test fun getOrDefault() {
+ val array = LongSparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrDefault(1L, default))
+ array.put(1L, "one")
+ assertEquals("one", array.getOrDefault(1L, default))
+ }
+
+ @Test fun getOrElse() {
+ val array = LongSparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrElse(1L) { default })
+ array.put(1L, "one")
+ assertEquals("one", array.getOrElse(1L) { fail() })
+ }
+
+ @Test fun isEmpty() {
+ val array = LongSparseArray<String>()
+ assertTrue(array.isEmpty())
+ array.put(1L, "one")
+ assertFalse(array.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.isNotEmpty())
+ array.put(1L, "one")
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = LongSparseArray<String>()
+ array.put(1L, "one")
+ assertFalse(array.remove(0L, "one"))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1L, "two"))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1L, "one"))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = LongSparseArray<String>()
+ val source = LongSparseArray<String>()
+ source.put(1L, "one")
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = LongSparseArray<String>()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val keys = mutableListOf<Long>()
+ val values = mutableListOf<String>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1L, 2L, 6L)
+ assertThat(values).containsExactly("one", "two", "six")
+ }
+
+ @Test fun keyIterator() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1L, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(2L, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(6L, iterator.nextLong())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = LongSparseArray<String>()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1L, "one")
+ array.put(2L, "two")
+ array.put(6L, "six")
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals("one", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("two", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("six", iterator.next())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt
new file mode 100644
index 0000000..198e13d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class LruCacheTest {
+ private data class TestData(val x: String = "bla")
+
+ @Test fun size() {
+ val cache = lruCache<String, TestData>(200, { k, (x) -> k.length * x.length })
+ cache.put("long", TestData())
+ assertEquals(cache.size(), 12)
+ }
+
+ @Test fun create() {
+ val cache = lruCache<String, TestData>(200, create = { key -> TestData("$key foo") })
+ assertEquals(cache.get("kung"), TestData("kung foo"))
+ }
+
+ @Test fun onEntryRemoved() {
+ var wasCalled = false
+
+ val cache = lruCache<String, TestData>(200, onEntryRemoved = { _, _, _, _ ->
+ wasCalled = true
+ })
+ val initial = TestData()
+ cache.put("a", initial)
+ cache.remove("a")
+ assertTrue(wasCalled)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt
new file mode 100644
index 0000000..e3fac39
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.util.Pair
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class PairTest {
+ @Test fun destructuringNonNull() {
+ val pair = Pair("one", "two")
+ val (first: String, second: String) = pair
+ assertSame(pair.first, first)
+ assertSame(pair.second, second)
+ }
+
+ @Test fun destructuringNullable() {
+ val pair = Pair("one", "two")
+ val (first: String?, second: String?) = pair
+ assertSame(pair.first, first)
+ assertSame(pair.second, second)
+ }
+
+ @Test fun toKotlin() {
+ val android = Pair("one", "two")
+ val kotlin = android.toKotlinPair()
+ assertEquals(android.first to android.second, kotlin)
+ }
+
+ @Test fun toAndroid() {
+ val kotlin = kotlin.Pair("one", "two")
+ val android = kotlin.toAndroidPair()
+ assertEquals(Pair(kotlin.first, kotlin.second), android)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt
new file mode 100644
index 0000000..ec5d238
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Range
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class RangeTest {
+ @Test fun infixFactory() {
+ val range: Range<String> = "a" rangeTo "c"
+ assertEquals("a", range.lower)
+ assertEquals("c", range.upper)
+ }
+
+ @Test fun extendValue() {
+ val range = ("a" rangeTo "c") + "e"
+ assertEquals("a", range.lower)
+ assertEquals("e", range.upper)
+ }
+
+ @Test fun extendRange() {
+ val range = ("a" rangeTo "c") + ("e" rangeTo "g")
+ assertEquals("a", range.lower)
+ assertEquals("g", range.upper)
+ }
+
+ @Test fun intersection() {
+ val range = ("a" rangeTo "e") and ("c" rangeTo "g")
+ assertEquals("c", range.lower)
+ assertEquals("e", range.upper)
+ }
+
+ @Test fun kotlinToAndroid() {
+ val range: Range<Int> = (1..3).toRange()
+ assertEquals(1, range.lower)
+ assertEquals(3, range.upper)
+ }
+
+ @Test fun androidToKotlin() {
+ val range: ClosedRange<String> = Range<String>("a", "c").toClosedRange()
+ assertEquals("a", range.start)
+ assertEquals("c", range.endInclusive)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
new file mode 100644
index 0000000..b38c747
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Size
+import android.util.SizeF
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class SizeTest {
+ @Test fun destructuringSize() {
+ val (w, h) = Size(320, 240)
+ assertEquals(320, w)
+ assertEquals(240, h)
+ }
+
+ @Test fun destructuringSizeF() {
+ val (w, h) = SizeF(1920.0f, 1080.0f)
+ assertEquals(1920.0f, w)
+ assertEquals(1080.0f, h)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt
new file mode 100644
index 0000000..4a49214
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.util.SparseArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseArrayTest {
+ @Test fun sizeProperty() {
+ val array = SparseArray<String>()
+ assertEquals(0, array.size)
+ array.put(1, "one")
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = SparseArray<String>()
+ assertFalse(1 in array)
+ array.put(1, "one")
+ assertTrue(1 in array)
+ }
+
+ @Test fun containsOperatorWithItem() {
+ val array = SparseArray<String>()
+
+ array.put(1, "one")
+ assertFalse(2 in array)
+
+ array.put(2, "two")
+ assertTrue(2 in array)
+ }
+
+ @Test fun setOperator() {
+ val array = SparseArray<String>()
+ array[1] = "one"
+ assertEquals("one", array.get(1))
+ }
+
+ @Test fun plusOperator() {
+ val first = SparseArray<String>().apply { put(1, "one") }
+ val second = SparseArray<String>().apply { put(2, "two") }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1, combined.keyAt(0))
+ assertEquals("one", combined.valueAt(0))
+ assertEquals(2, combined.keyAt(1))
+ assertEquals("two", combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = SparseArray<String>()
+ assertFalse(array.containsKey(1))
+ array.put(1, "one")
+ assertTrue(array.containsKey(1))
+ }
+
+ @Test fun containsValue() {
+ val array = SparseArray<String>()
+ assertFalse(array.containsValue("one"))
+ array.put(1, "one")
+ assertTrue(array.containsValue("one"))
+ }
+
+ @Test fun getOrDefault() {
+ val array = SparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrDefault(1, default))
+ array.put(1, "one")
+ assertEquals("one", array.getOrDefault(1, default))
+ }
+
+ @Test fun getOrElse() {
+ val array = SparseArray<Any>()
+ val default = Any()
+ assertSame(default, array.getOrElse(1) { default })
+ array.put(1, "one")
+ assertEquals("one", array.getOrElse(1) { fail() })
+ }
+
+ @Test fun isEmpty() {
+ val array = SparseArray<String>()
+ assertTrue(array.isEmpty())
+ array.put(1, "one")
+ assertFalse(array.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ val array = SparseArray<String>()
+ assertFalse(array.isNotEmpty())
+ array.put(1, "one")
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = SparseArray<String>()
+ array.put(1, "one")
+ assertFalse(array.remove(0, "one"))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1, "two"))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1, "one"))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = SparseArray<String>()
+ val source = SparseArray<String>()
+ source.put(1, "one")
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = SparseArray<String>()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val keys = mutableListOf<Int>()
+ val values = mutableListOf<String>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1, 2, 6)
+ assertThat(values).containsExactly("one", "two", "six")
+ }
+
+ @Test fun keyIterator() {
+ val array = SparseArray<String>()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(2, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(6, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = SparseArray<String>()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1, "one")
+ array.put(2, "two")
+ array.put(6, "six")
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals("one", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("two", iterator.next())
+ assertTrue(iterator.hasNext())
+ assertEquals("six", iterator.next())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt
new file mode 100644
index 0000000..1d67493
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.util.SparseBooleanArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseBooleanArrayTest {
+ @Test fun sizeProperty() {
+ val array = SparseBooleanArray()
+ assertEquals(0, array.size)
+ array.put(1, true)
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = SparseBooleanArray()
+ assertFalse(1 in array)
+ array.put(1, true)
+ assertTrue(1 in array)
+ }
+
+ @Test fun containsOperatorWithValue() {
+ val array = SparseBooleanArray()
+
+ array.put(1, true)
+ assertFalse(2 in array)
+
+ array.put(2, true)
+ assertTrue(2 in array)
+ }
+
+ @Test fun setOperator() {
+ val array = SparseBooleanArray()
+ array[1] = true
+ assertTrue(array.get(1))
+ }
+
+ @Test fun plusOperator() {
+ val first = SparseBooleanArray().apply { put(1, true) }
+ val second = SparseBooleanArray().apply { put(2, false) }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1, combined.keyAt(0))
+ assertTrue(combined.valueAt(0))
+ assertEquals(2, combined.keyAt(1))
+ assertFalse(combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = SparseBooleanArray()
+ assertFalse(array.containsKey(1))
+ array.put(1, true)
+ assertTrue(array.containsKey(1))
+ }
+
+ @Test fun containsKeyWithValue() {
+ val array = SparseBooleanArray()
+
+ array.put(1, true)
+ assertFalse(array.containsKey(2))
+
+ array.put(2, true)
+ assertTrue(array.containsKey(2))
+ }
+
+ @Test fun containsValue() {
+ val array = SparseBooleanArray()
+ assertFalse(array.containsValue(true))
+ array.put(1, true)
+ assertTrue(array.containsValue(true))
+ }
+
+ @Test fun getOrDefault() {
+ val array = SparseBooleanArray()
+ assertFalse(array.getOrDefault(1, false))
+ array.put(1, true)
+ assertTrue(array.getOrDefault(1, false))
+ }
+
+ @Test fun getOrElse() {
+ val array = SparseBooleanArray()
+ assertFalse(array.getOrElse(1) { false })
+ array.put(1, true)
+ assertTrue(array.getOrElse(1) { fail() })
+ }
+
+ @Test fun isEmpty() {
+ val array = SparseBooleanArray()
+ assertTrue(array.isEmpty())
+ array.put(1, true)
+ assertFalse(array.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ val array = SparseBooleanArray()
+ assertFalse(array.isNotEmpty())
+ array.put(1, true)
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = SparseBooleanArray()
+ array.put(1, true)
+ assertFalse(array.remove(0, true))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1, false))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1, true))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = SparseBooleanArray()
+ val source = SparseBooleanArray()
+ source.put(1, true)
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = SparseBooleanArray()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1, true)
+ array.put(2, false)
+ array.put(6, true)
+
+ val keys = mutableListOf<Int>()
+ val values = mutableListOf<Boolean>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1, 2, 6)
+ assertThat(values).containsExactly(true, false, true)
+ }
+
+ @Test fun keyIterator() {
+ val array = SparseBooleanArray()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1, true)
+ array.put(2, false)
+ array.put(6, true)
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(2, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(6, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = SparseBooleanArray()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1, true)
+ array.put(2, false)
+ array.put(6, true)
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertTrue(iterator.nextBoolean())
+ assertTrue(iterator.hasNext())
+ assertFalse(iterator.nextBoolean())
+ assertTrue(iterator.hasNext())
+ assertTrue(iterator.nextBoolean())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt
new file mode 100644
index 0000000..40794c1
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.util.SparseIntArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseIntArrayTest {
+ @Test fun sizeProperty() {
+ val array = SparseIntArray()
+ assertEquals(0, array.size)
+ array.put(1, 11)
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = SparseIntArray()
+ assertFalse(1 in array)
+ array.put(1, 11)
+ assertTrue(1 in array)
+ }
+
+ @Test fun containsOperatorWithValue() {
+ val array = SparseIntArray()
+
+ array.put(1, 11)
+ assertFalse(2 in array)
+
+ array.put(2, 22)
+ assertTrue(2 in array)
+ }
+
+ @Test fun setOperator() {
+ val array = SparseIntArray()
+ array[1] = 11
+ assertEquals(11, array.get(1))
+ }
+
+ @Test fun plusOperator() {
+ val first = SparseIntArray().apply { put(1, 11) }
+ val second = SparseIntArray().apply { put(2, 22) }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1, combined.keyAt(0))
+ assertEquals(11, combined.valueAt(0))
+ assertEquals(2, combined.keyAt(1))
+ assertEquals(22, combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = SparseIntArray()
+ assertFalse(array.containsKey(1))
+ array.put(1, 11)
+ assertTrue(array.containsKey(1))
+ }
+
+ @Test fun containsKeyWithValue() {
+ val array = SparseIntArray()
+
+ array.put(1, 11)
+ assertFalse(array.containsKey(2))
+
+ array.put(2, 22)
+ assertTrue(array.containsKey(2))
+ }
+
+ @Test fun containsValue() {
+ val array = SparseIntArray()
+ assertFalse(array.containsValue(11))
+ array.put(1, 11)
+ assertTrue(array.containsValue(11))
+ }
+
+ @Test fun getOrDefault() {
+ val array = SparseIntArray()
+ assertEquals(22, array.getOrDefault(1, 22))
+ array.put(1, 11)
+ assertEquals(11, array.getOrDefault(1, 22))
+ }
+
+ @Test fun getOrElse() {
+ val array = SparseIntArray()
+ assertEquals(22, array.getOrElse(1) { 22 })
+ array.put(1, 11)
+ assertEquals(11, array.getOrElse(1) { fail() })
+ }
+
+ @Test fun isEmpty() {
+ val array = SparseIntArray()
+ assertTrue(array.isEmpty())
+ array.put(1, 11)
+ assertFalse(array.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ val array = SparseIntArray()
+ assertFalse(array.isNotEmpty())
+ array.put(1, 11)
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = SparseIntArray()
+ array.put(1, 11)
+ assertFalse(array.remove(0, 11))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1, 22))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1, 11))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = SparseIntArray()
+ val source = SparseIntArray()
+ source.put(1, 11)
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = SparseIntArray()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1, 11)
+ array.put(2, 22)
+ array.put(6, 66)
+
+ val keys = mutableListOf<Int>()
+ val values = mutableListOf<Int>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1, 2, 6)
+ assertThat(values).containsExactly(11, 22, 66)
+ }
+
+ @Test fun keyIterator() {
+ val array = SparseIntArray()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1, 11)
+ array.put(2, 22)
+ array.put(6, 66)
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(2, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(6, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = SparseIntArray()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1, 11)
+ array.put(2, 22)
+ array.put(6, 66)
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(11, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(22, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(66, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt
new file mode 100644
index 0000000..c65c2ad
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.SparseLongArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 18)
+class SparseLongArrayTest {
+ @Test fun sizeProperty() {
+ val array = SparseLongArray()
+ assertEquals(0, array.size)
+ array.put(1, 11L)
+ assertEquals(1, array.size)
+ }
+
+ @Test fun containsOperator() {
+ val array = SparseLongArray()
+ assertFalse(1 in array)
+ array.put(1, 11L)
+ assertTrue(1 in array)
+ }
+
+ @Test fun containsOperatorWithValue() {
+ val array = SparseLongArray()
+
+ array.put(1, 11L)
+ assertFalse(2 in array)
+
+ array.put(2, 22L)
+ assertTrue(2 in array)
+ }
+
+ @Test fun setOperator() {
+ val array = SparseLongArray()
+ array[1] = 11L
+ assertEquals(11L, array.get(1))
+ }
+
+ @Test fun plusOperator() {
+ val first = SparseLongArray().apply { put(1, 11L) }
+ val second = SparseLongArray().apply { put(2, 22L) }
+ val combined = first + second
+ assertEquals(2, combined.size())
+ assertEquals(1, combined.keyAt(0))
+ assertEquals(11L, combined.valueAt(0))
+ assertEquals(2, combined.keyAt(1))
+ assertEquals(22L, combined.valueAt(1))
+ }
+
+ @Test fun containsKey() {
+ val array = SparseLongArray()
+ assertFalse(array.containsKey(1))
+ array.put(1, 11L)
+ assertTrue(array.containsKey(1))
+ }
+
+ @Test fun containsKeyWithValue() {
+ val array = SparseLongArray()
+
+ array.put(1, 11L)
+ assertFalse(array.containsKey(2))
+
+ array.put(2, 22L)
+ assertTrue(array.containsKey(2))
+ }
+
+ @Test fun containsValue() {
+ val array = SparseLongArray()
+ assertFalse(array.containsValue(11L))
+ array.put(1, 11L)
+ assertTrue(array.containsValue(11L))
+ }
+
+ @Test fun getOrDefault() {
+ val array = SparseLongArray()
+ assertEquals(22L, array.getOrDefault(1, 22L))
+ array.put(1, 11L)
+ assertEquals(11L, array.getOrDefault(1, 22L))
+ }
+
+ @Test fun getOrElse() {
+ val array = SparseLongArray()
+ assertEquals(22L, array.getOrElse(1) { 22L })
+ array.put(1, 11L)
+ assertEquals(11L, array.getOrElse(1) { fail() })
+ }
+
+ @Test fun isEmpty() {
+ val array = SparseLongArray()
+ assertTrue(array.isEmpty())
+ array.put(1, 11L)
+ assertFalse(array.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ val array = SparseLongArray()
+ assertFalse(array.isNotEmpty())
+ array.put(1, 11L)
+ assertTrue(array.isNotEmpty())
+ }
+
+ @Test fun removeValue() {
+ val array = SparseLongArray()
+ array.put(1, 11L)
+ assertFalse(array.remove(0, 11L))
+ assertEquals(1, array.size())
+ assertFalse(array.remove(1, 22L))
+ assertEquals(1, array.size())
+ assertTrue(array.remove(1, 11L))
+ assertEquals(0, array.size())
+ }
+
+ @Test fun putAll() {
+ val dest = SparseLongArray()
+ val source = SparseLongArray()
+ source.put(1, 11L)
+
+ assertEquals(0, dest.size())
+ dest.putAll(source)
+ assertEquals(1, dest.size())
+ }
+
+ @Test fun forEach() {
+ val array = SparseLongArray()
+ array.forEach { _, _ -> fail() }
+
+ array.put(1, 11L)
+ array.put(2, 22L)
+ array.put(6, 66L)
+
+ val keys = mutableListOf<Int>()
+ val values = mutableListOf<Long>()
+ array.forEach { key, value ->
+ keys.add(key)
+ values.add(value)
+ }
+ assertThat(keys).containsExactly(1, 2, 6)
+ assertThat(values).containsExactly(11L, 22L, 66L)
+ }
+
+ @Test fun keyIterator() {
+ val array = SparseLongArray()
+ assertFalse(array.keyIterator().hasNext())
+
+ array.put(1, 11L)
+ array.put(2, 22L)
+ array.put(6, 66L)
+
+ val iterator = array.keyIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(1, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(2, iterator.nextInt())
+ assertTrue(iterator.hasNext())
+ assertEquals(6, iterator.nextInt())
+ assertFalse(iterator.hasNext())
+ }
+
+ @Test fun valueIterator() {
+ val array = SparseLongArray()
+ assertFalse(array.valueIterator().hasNext())
+
+ array.put(1, 11L)
+ array.put(2, 22L)
+ array.put(6, 66L)
+
+ val iterator = array.valueIterator()
+ assertTrue(iterator.hasNext())
+ assertEquals(11, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(22, iterator.nextLong())
+ assertTrue(iterator.hasNext())
+ assertEquals(66, iterator.nextLong())
+ assertFalse(iterator.hasNext())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt b/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt
new file mode 100644
index 0000000..81996b8
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt
@@ -0,0 +1,14 @@
+package androidx.core.view
+
+import android.content.Context
+import android.view.View
+
+class AccessibilityAnnouncementCapturingView(context: Context?) : View(context) {
+
+ var announcement: CharSequence? = null
+
+ override fun announceForAccessibility(text: CharSequence?) {
+ super.announceForAccessibility(text)
+ announcement = text
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
new file mode 100644
index 0000000..18e1b56
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.view
+
+import android.support.test.InstrumentationRegistry
+import android.view.Menu.NONE
+import android.view.MenuItem
+import android.widget.Toolbar
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class MenuTest {
+ private val menu = Toolbar(InstrumentationRegistry.getContext()).menu
+
+ @Test fun get() {
+ val item = menu.add("")
+ assertSame(item, menu[0])
+ }
+
+ @Test fun contains() {
+ val item1 = menu.add("")
+ assertTrue(item1 in menu)
+
+ val item2 = menu.add("")
+ assertTrue(item2 in menu)
+ }
+
+ @Test fun size() {
+ assertEquals(0, menu.size)
+
+ menu.add("")
+ assertEquals(1, menu.size)
+
+ menu.add(NONE, 123, NONE, "")
+ assertEquals(2, menu.size)
+
+ menu.removeItem(123)
+ assertEquals(1, menu.size)
+ }
+
+ @Test fun isEmpty() {
+ assertTrue(menu.isEmpty())
+ menu.add("")
+ assertFalse(menu.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ assertFalse(menu.isNotEmpty())
+ menu.add("")
+ assertTrue(menu.isNotEmpty())
+ }
+
+ @Test fun forEach() {
+ menu.forEach {
+ fail("Empty menu should not invoke lambda")
+ }
+
+ val item1 = menu.add("")
+ val item2 = menu.add("")
+
+ val items = mutableListOf<MenuItem>()
+ menu.forEach {
+ items += it
+ }
+ assertThat(items).containsExactly(item1, item2)
+ }
+
+ @Test fun forEachIndexed() {
+ menu.forEachIndexed { _, _ ->
+ fail("Empty menu should not invoke lambda")
+ }
+
+ val item1 = menu.add("")
+ val item2 = menu.add("")
+
+ val items = mutableListOf<MenuItem>()
+ menu.forEachIndexed { index, item ->
+ assertEquals(index, items.size)
+ items += item
+ }
+ assertThat(items).containsExactly(item1, item2)
+ }
+
+ @Test fun iterator() {
+ val item1 = menu.add("")
+ val item2 = menu.add("")
+
+ val iterator = menu.iterator()
+ assertTrue(iterator.hasNext())
+ assertSame(item1, iterator.next())
+ assertTrue(iterator.hasNext())
+ assertSame(item2, iterator.next())
+ assertFalse(iterator.hasNext())
+ assertThrows<IndexOutOfBoundsException> {
+ iterator.next()
+ }
+ }
+
+ @Test fun iteratorRemoving() {
+ val item1 = menu.add("")
+ val item2 = menu.add("")
+
+ val iterator = menu.iterator()
+
+ assertSame(item1, iterator.next())
+ iterator.remove()
+ assertFalse(item1 in menu)
+ assertEquals(1, menu.size())
+
+ assertSame(item2, iterator.next())
+ iterator.remove()
+ assertFalse(item2 in menu)
+ assertEquals(0, menu.size())
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt
new file mode 100644
index 0000000..7ec791d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.view
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ViewGroupTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val viewGroup = LinearLayout(context)
+
+ @Test fun get() {
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ assertSame(view1, viewGroup[0])
+ assertSame(view2, viewGroup[1])
+
+ assertThrows<IndexOutOfBoundsException> {
+ viewGroup[-1]
+ }.hasMessageThat().isEqualTo("Index: -1, Size: 2")
+
+ assertThrows<IndexOutOfBoundsException> {
+ viewGroup[2]
+ }.hasMessageThat().isEqualTo("Index: 2, Size: 2")
+ }
+
+ @Test fun contains() {
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ assertTrue(view1 in viewGroup)
+ assertFalse(view1 !in viewGroup)
+
+ val view2 = View(context)
+ assertFalse(view2 in viewGroup)
+ assertTrue(view2 !in viewGroup)
+ }
+
+ @Test fun plusAssign() {
+ assertEquals(0, viewGroup.childCount)
+
+ val view1 = View(context)
+ viewGroup += view1
+ assertEquals(1, viewGroup.childCount)
+ assertSame(view1, viewGroup.getChildAt(0))
+
+ val view2 = View(context)
+ viewGroup += view2
+ assertEquals(2, viewGroup.childCount)
+ assertSame(view2, viewGroup.getChildAt(1))
+ }
+
+ @Test fun minusAssign() {
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ assertEquals(2, viewGroup.childCount)
+
+ viewGroup -= view2
+ assertEquals(1, viewGroup.childCount)
+ assertSame(view1, viewGroup.getChildAt(0))
+
+ viewGroup -= view1
+ assertEquals(0, viewGroup.childCount)
+ }
+
+ @Test fun size() {
+ assertEquals(0, viewGroup.size)
+
+ viewGroup.addView(View(context))
+ assertEquals(1, viewGroup.size)
+
+ viewGroup.addView(View(context))
+ assertEquals(2, viewGroup.size)
+
+ viewGroup.removeViewAt(0)
+ assertEquals(1, viewGroup.size)
+ }
+
+ @Test fun isEmpty() {
+ assertTrue(viewGroup.isEmpty())
+ viewGroup.addView(View(context))
+ assertFalse(viewGroup.isEmpty())
+ }
+
+ @Test fun isNotEmpty() {
+ assertFalse(viewGroup.isNotEmpty())
+ viewGroup.addView(View(context))
+ assertTrue(viewGroup.isNotEmpty())
+ }
+
+ @Test fun forEach() {
+ viewGroup.forEach {
+ fail("Empty view group should not invoke lambda")
+ }
+
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ val views = mutableListOf<View>()
+ viewGroup.forEach {
+ views += it
+ }
+ assertThat(views).containsExactly(view1, view2)
+ }
+
+ @Test fun forEachIndexed() {
+ viewGroup.forEachIndexed { _, _ ->
+ fail("Empty view group should not invoke lambda")
+ }
+
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ val views = mutableListOf<View>()
+ viewGroup.forEachIndexed { index, view ->
+ assertEquals(index, views.size)
+ views += view
+ }
+ assertThat(views).containsExactly(view1, view2)
+ }
+
+ @Test fun iterator() {
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ val iterator = viewGroup.iterator()
+ assertTrue(iterator.hasNext())
+ assertSame(view1, iterator.next())
+ assertTrue(iterator.hasNext())
+ assertSame(view2, iterator.next())
+ assertFalse(iterator.hasNext())
+ assertThrows<IndexOutOfBoundsException> {
+ iterator.next()
+ }
+ }
+
+ @Test fun iteratorRemoving() {
+ val view1 = View(context)
+ viewGroup.addView(view1)
+ val view2 = View(context)
+ viewGroup.addView(view2)
+
+ val iterator = viewGroup.iterator()
+
+ assertSame(view1, iterator.next())
+ iterator.remove()
+ assertFalse(view1 in viewGroup)
+ assertEquals(1, viewGroup.childCount)
+
+ assertSame(view2, iterator.next())
+ iterator.remove()
+ assertFalse(view2 in viewGroup)
+ assertEquals(0, viewGroup.childCount)
+ }
+
+ @Test fun iteratorForEach() {
+ val views = listOf(View(context), View(context))
+ views.forEach(viewGroup::addView)
+
+ var index = 0
+ for (view in viewGroup) {
+ assertSame(views[index++], view)
+ }
+ }
+
+ @Test fun children() {
+ val views = listOf(View(context), View(context), View(context))
+ views.forEach { viewGroup.addView(it) }
+
+ viewGroup.children.forEachIndexed { index, child ->
+ assertSame(views[index], child)
+ }
+ }
+
+ @Test fun setMargins() {
+ val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+ layoutParams.setMargins(42)
+ assertEquals(42, layoutParams.leftMargin)
+ assertEquals(42, layoutParams.topMargin)
+ assertEquals(42, layoutParams.rightMargin)
+ assertEquals(42, layoutParams.bottomMargin)
+ }
+
+ @Test fun updateMargins() {
+ val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+ layoutParams.updateMargins(top = 10, right = 20)
+ assertEquals(0, layoutParams.leftMargin)
+ assertEquals(10, layoutParams.topMargin)
+ assertEquals(20, layoutParams.rightMargin)
+ assertEquals(0, layoutParams.bottomMargin)
+ }
+
+ @Test fun updateMarginsNoOp() {
+ val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+ layoutParams.setMargins(10, 20, 30, 40)
+ layoutParams.updateMargins()
+ assertEquals(10, layoutParams.leftMargin)
+ assertEquals(20, layoutParams.topMargin)
+ assertEquals(30, layoutParams.rightMargin)
+ assertEquals(40, layoutParams.bottomMargin)
+ }
+
+ @SdkSuppress(minSdkVersion = 17)
+ @Test fun updateMarginsRelative() {
+ val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+ layoutParams.updateMarginsRelative(start = 10, end = 20)
+ assertEquals(0, layoutParams.leftMargin)
+ assertEquals(0, layoutParams.topMargin)
+ assertEquals(0, layoutParams.rightMargin)
+ assertEquals(0, layoutParams.bottomMargin)
+ assertEquals(10, layoutParams.marginStart)
+ assertEquals(20, layoutParams.marginEnd)
+ assertTrue(layoutParams.isMarginRelative)
+ }
+
+ @SdkSuppress(minSdkVersion = 17)
+ @Test fun updateMarginsRelativeNoOp() {
+ val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+ layoutParams.setMargins(10, 20, 30, 40)
+ layoutParams.updateMarginsRelative()
+ assertEquals(10, layoutParams.leftMargin)
+ assertEquals(20, layoutParams.topMargin)
+ assertEquals(30, layoutParams.rightMargin)
+ assertEquals(40, layoutParams.bottomMargin)
+ assertEquals(10, layoutParams.marginStart)
+ assertEquals(30, layoutParams.marginEnd)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt
new file mode 100644
index 0000000..0b7cfa2
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.view
+
+import android.graphics.Bitmap
+import android.support.test.InstrumentationRegistry
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import androidx.core.ktx.test.R
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ViewTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val view = View(context)
+
+ @Test
+ fun doOnNextLayout() {
+ var calls = 0
+ view.doOnNextLayout {
+ calls++
+ }
+ view.layout(0, 0, 10, 10)
+ assertEquals(1, calls)
+
+ // Now layout again and make sure that the listener was removed
+ view.layout(0, 0, 10, 10)
+ assertEquals(1, calls)
+ }
+
+ @Test
+ fun doOnLayoutBeforeLayout() {
+ var called = false
+ view.doOnLayout {
+ called = true
+ }
+ view.layout(0, 0, 10, 10)
+ assertTrue(called)
+ }
+
+ @Test
+ fun doOnLayoutAfterLayout() {
+ view.layout(0, 0, 10, 10)
+
+ var called = false
+ view.doOnLayout {
+ called = true
+ }
+ assertTrue(called)
+ }
+
+ @Test
+ fun doOnLayoutWhileLayoutRequested() {
+ // First layout the view
+ view.layout(0, 0, 10, 10)
+ // Then later a layout is requested
+ view.requestLayout()
+
+ var called = false
+ view.doOnLayout {
+ called = true
+ }
+
+ // Assert that we haven't been called while the layout pass is pending
+ assertFalse(called)
+
+ // Now layout the view and assert that we're called
+ view.layout(0, 0, 20, 20)
+ assertTrue(called)
+ }
+
+ @Test
+ fun doOnPreDraw() {
+ var calls = 0
+ view.doOnPreDraw {
+ calls++
+ }
+ view.viewTreeObserver.dispatchOnPreDraw()
+ assertEquals(1, calls)
+
+ // Now dispatch again to make sure that the listener was removed
+ view.viewTreeObserver.dispatchOnPreDraw()
+ assertEquals(1, calls)
+ }
+
+ @Test
+ fun setPadding() {
+ view.setPadding(42)
+ assertEquals(42, view.paddingLeft)
+ assertEquals(42, view.paddingTop)
+ assertEquals(42, view.paddingRight)
+ assertEquals(42, view.paddingBottom)
+ }
+
+ @Test
+ fun updatePadding() {
+ view.updatePadding(top = 10, right = 20)
+ assertEquals(0, view.paddingLeft)
+ assertEquals(10, view.paddingTop)
+ assertEquals(20, view.paddingRight)
+ assertEquals(0, view.paddingBottom)
+ }
+
+ @Test
+ fun updatePaddingNoOp() {
+ view.setPadding(10, 20, 30, 40)
+ view.updatePadding()
+ assertEquals(10, view.paddingLeft)
+ assertEquals(20, view.paddingTop)
+ assertEquals(30, view.paddingRight)
+ assertEquals(40, view.paddingBottom)
+ }
+
+ @Test
+ fun updatePaddingRelative() {
+ view.updatePaddingRelative(start = 10, end = 20)
+ assertEquals(10, view.paddingStart)
+ assertEquals(0, view.paddingTop)
+ assertEquals(20, view.paddingEnd)
+ assertEquals(0, view.paddingBottom)
+ }
+
+ @Test
+ fun updatePaddingRelativeNoOp() {
+ view.setPaddingRelative(10, 20, 30, 40)
+ view.updatePaddingRelative()
+ assertEquals(10, view.paddingStart)
+ assertEquals(20, view.paddingTop)
+ assertEquals(30, view.paddingEnd)
+ assertEquals(40, view.paddingBottom)
+ }
+
+ @Test
+ fun toBitmapBeforeLayout() {
+ assertThrows<IllegalStateException> {
+ view.toBitmap()
+ }
+ }
+
+ @Test
+ fun toBitmap() {
+ view.layout(0, 0, 100, 100)
+ val bitmap = view.toBitmap()
+
+ assertEquals(100, bitmap.width)
+ assertEquals(100, bitmap.height)
+ }
+
+ @Test
+ fun toBitmapCustomConfig() {
+ view.layout(0, 0, 100, 100)
+ val bitmap = view.toBitmap(Bitmap.Config.RGB_565)
+
+ assertSame(Bitmap.Config.RGB_565, bitmap.config)
+ }
+
+ @Test fun isVisible() {
+ view.isVisible = true
+ assertTrue(view.isVisible)
+ assertEquals(View.VISIBLE, view.visibility)
+
+ view.isVisible = false
+ assertFalse(view.isVisible)
+ assertEquals(View.GONE, view.visibility)
+ }
+
+ @Test fun isInvisible() {
+ view.isInvisible = true
+ assertTrue(view.isInvisible)
+ assertEquals(View.INVISIBLE, view.visibility)
+
+ view.isInvisible = false
+ assertFalse(view.isInvisible)
+ assertEquals(View.VISIBLE, view.visibility)
+ }
+
+ @Test fun isGone() {
+ view.isGone = true
+ assertTrue(view.isGone)
+ assertEquals(View.GONE, view.visibility)
+
+ view.isGone = false
+ assertFalse(view.isGone)
+ assertEquals(View.VISIBLE, view.visibility)
+ }
+
+ @Test fun updateLayoutParams() {
+ view.layoutParams = ViewGroup.LayoutParams(0, 0)
+ view.updateLayoutParams {
+ assertSame(view.layoutParams, this)
+
+ width = 500
+ height = 1000
+ }
+
+ assertEquals(500, view.layoutParams.width)
+ assertEquals(1000, view.layoutParams.height)
+ }
+
+ @Test fun updateLayoutParamsAsType() {
+ view.layoutParams = LinearLayout.LayoutParams(0, 0)
+ view.updateLayoutParams<LinearLayout.LayoutParams> {
+ assertSame(view.layoutParams, this)
+
+ weight = 2f
+ }
+
+ assertEquals(2f, (view.layoutParams as LinearLayout.LayoutParams).weight)
+ }
+
+ @Test fun updateLayoutParamsWrongType() {
+ assertThrows<ClassCastException> {
+ view.updateLayoutParams<RelativeLayout.LayoutParams> {
+ fail()
+ }
+ }
+ }
+
+ @Test fun announceForAccessibility() {
+ val testView = AccessibilityAnnouncementCapturingView(context)
+
+ testView.announceForAccessibility(R.string.text)
+
+ val resolvedText = context.getText(R.string.text)
+ assertEquals(testView.announcement, resolvedText)
+ }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt b/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt
new file mode 100644
index 0000000..f8e9931
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.widget
+
+import android.support.test.InstrumentationRegistry
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.ktx.test.R
+import androidx.core.view.forEach
+import androidx.core.view.isVisible
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ToastTest {
+ private val context = InstrumentationRegistry.getContext()
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ @Test
+ fun createToastWithTextShort() = instrumentation.runOnMainSync {
+ val toast = context.toast("Short Toast")
+ assertEquals(Toast.LENGTH_SHORT, toast.duration)
+ assertTrue(containsText(toast.view, "Short Toast"))
+ assertTrue(toast.view.isVisible)
+ }
+
+ @Test
+ fun createToastWithTextLong() = instrumentation.runOnMainSync {
+ val toast = context.toast("Long Toast", Toast.LENGTH_LONG)
+ assertEquals(Toast.LENGTH_LONG, toast.duration)
+ assertTrue(containsText(toast.view, "Long Toast"))
+ assertTrue(toast.view.isVisible)
+ }
+
+ @Test
+ fun createToastWithResIdShort() = instrumentation.runOnMainSync {
+ val toast = context.toast(R.string.text)
+ assertEquals(Toast.LENGTH_SHORT, toast.duration)
+ assertTrue(containsText(toast.view, context.getString(R.string.text)))
+ assertTrue(toast.view.isVisible)
+ }
+
+ @Test
+ fun createToastWithResIdLong() = instrumentation.runOnMainSync {
+ val toast = context.toast(R.string.text, Toast.LENGTH_LONG)
+ assertEquals(Toast.LENGTH_LONG, toast.duration)
+ assertTrue(containsText(toast.view, context.getString(R.string.text)))
+ assertTrue(toast.view.isVisible)
+ }
+
+ private fun containsText(view: View, text: String): Boolean {
+ if (view is TextView && view.text == text) {
+ return true
+ }
+ if (view is ViewGroup) {
+ view.forEach {
+ if (containsText(it, text)) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+}
diff --git a/core/ktx/src/androidTest/res/drawable/box.xml b/core/ktx/src/androidTest/res/drawable/box.xml
new file mode 100644
index 0000000..3915878
--- /dev/null
+++ b/core/ktx/src/androidTest/res/drawable/box.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <size android:height="10px" android:width="10px"/>
+ <solid android:color="#fff"/>
+</shape>
diff --git a/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf b/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf
new file mode 100644
index 0000000..fc981ce
--- /dev/null
+++ b/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf
Binary files differ
diff --git a/core/ktx/src/androidTest/res/layout/test_activity.xml b/core/ktx/src/androidTest/res/layout/test_activity.xml
new file mode 100644
index 0000000..a477350
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/test_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView android:id="@+id/image_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@android:drawable/ic_media_play"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/ktx/src/androidTest/res/layout/test_attrs.xml b/core/ktx/src/androidTest/res/layout/test_attrs.xml
new file mode 100644
index 0000000..d5975c5
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/test_attrs.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/root"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:sample="42" />
diff --git a/core/ktx/src/androidTest/res/layout/typed_array.xml b/core/ktx/src/androidTest/res/layout/typed_array.xml
new file mode 100644
index 0000000..e485ffd
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/typed_array.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ app:boolean_present="true"
+ app:color_present="#fff"
+ app:dimension_present="1px"
+ app:drawable_present="@drawable/box"
+ app:string_present="Hello"
+ app:float_present="0.1"
+ app:integer_present="1"
+ app:resource_present="@font/inconsolata_regular"
+ app:font_present="@font/inconsolata_regular"
+ app:text_array_present="@array/text_array"
+/>
diff --git a/core/ktx/src/androidTest/res/values/attrs.xml b/core/ktx/src/androidTest/res/values/attrs.xml
new file mode 100644
index 0000000..e3f9723
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <declare-styleable name="SampleAttrs">
+ <attr name="sample" format="integer" />
+ </declare-styleable>
+</resources>
diff --git a/core/ktx/src/androidTest/res/values/strings.xml b/core/ktx/src/androidTest/res/values/strings.xml
new file mode 100644
index 0000000..20877e9
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="text">Hello World</string>
+ <string-array name="text_array">
+ <item>Hello</item>
+ <item>World</item>
+ </string-array>
+</resources>
diff --git a/core/ktx/src/androidTest/res/values/styles.xml b/core/ktx/src/androidTest/res/values/styles.xml
new file mode 100644
index 0000000..ea35e47
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/styles.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <declare-styleable name="TypedArrayTypes">
+ <attr name="boolean_present" format="boolean"/>
+ <attr name="boolean_absent" format="boolean"/>
+
+ <attr name="color_present" format="color"/>
+ <attr name="color_absent" format="color"/>
+
+ <attr name="dimension_present" format="dimension"/>
+ <attr name="dimension_absent" format="dimension"/>
+
+ <attr name="drawable_present" format="reference"/>
+ <attr name="drawable_absent" format="reference"/>
+
+ <attr name="float_present" format="float"/>
+ <attr name="float_absent" format="float"/>
+
+ <attr name="font_present" format="reference"/>
+ <attr name="font_absent" format="reference"/>
+
+ <attr name="integer_present" format="integer"/>
+ <attr name="integer_absent" format="integer"/>
+
+ <attr name="resource_present" format="reference"/>
+ <attr name="resource_absent" format="reference"/>
+
+ <attr name="string_present" format="string"/>
+ <attr name="string_absent" format="string"/>
+
+ <attr name="text_array_present" format="reference"/>
+ <attr name="text_array_absent" format="reference"/>
+ </declare-styleable>
+</resources>
diff --git a/core/ktx/src/androidTest/res/xml/preferences.xml b/core/ktx/src/androidTest/res/xml/preferences.xml
new file mode 100644
index 0000000..33629e1
--- /dev/null
+++ b/core/ktx/src/androidTest/res/xml/preferences.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<PreferenceScreen/>
\ No newline at end of file
diff --git a/core/ktx/src/main/AndroidManifest.xml b/core/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7f93e5e
--- /dev/null
+++ b/core/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="androidx.core.ktx"/>
diff --git a/core/ktx/src/main/java/androidx/core/animation/Animator.kt b/core/ktx/src/main/java/androidx/core/animation/Animator.kt
new file mode 100644
index 0000000..1438bcd
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/animation/Animator.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.animation
+
+import android.animation.Animator
+import androidx.annotation.RequiresApi
+
+/**
+ * Add an action which will be invoked when the animation has ended.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.end
+ */
+fun Animator.doOnEnd(action: (animator: Animator) -> Unit) = addListener(onEnd = action)
+
+/**
+ * Add an action which will be invoked when the animation has started.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.start
+ */
+fun Animator.doOnStart(action: (animator: Animator) -> Unit) = addListener(onStart = action)
+
+/**
+ * Add an action which will be invoked when the animation has been cancelled.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.cancel
+ */
+fun Animator.doOnCancel(action: (animator: Animator) -> Unit) = addListener(onCancel = action)
+
+/**
+ * Add an action which will be invoked when the animation has repeated.
+ * @return the [Animator.AnimatorListener] added to the Animator
+ */
+fun Animator.doOnRepeat(action: (animator: Animator) -> Unit) = addListener(onRepeat = action)
+
+/**
+ * Add an action which will be invoked when the animation has resumed after a pause.
+ *
+ * @return the [Animator.AnimatorPauseListener] added to the Animator
+ * @see Animator.resume
+ */
+@RequiresApi(19)
+fun Animator.doOnResume(action: (animator: Animator) -> Unit) = addPauseListener(onResume = action)
+
+/**
+ * Add an action which will be invoked when the animation has been paused.
+ *
+ * @return the [Animator.AnimatorPauseListener] added to the Animator
+ * @see Animator.pause
+ */
+@RequiresApi(19)
+fun Animator.doOnPause(action: (animator: Animator) -> Unit) = addPauseListener(onPause = action)
+
+/**
+ * Add a listener to this Animator using the provided actions.
+ */
+fun Animator.addListener(
+ onEnd: ((animator: Animator) -> Unit)? = null,
+ onStart: ((animator: Animator) -> Unit)? = null,
+ onCancel: ((animator: Animator) -> Unit)? = null,
+ onRepeat: ((animator: Animator) -> Unit)? = null
+): Animator.AnimatorListener {
+ val listener = object : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animator: Animator) {
+ onRepeat?.invoke(animator)
+ }
+
+ override fun onAnimationEnd(animator: Animator) {
+ onEnd?.invoke(animator)
+ }
+
+ override fun onAnimationCancel(animator: Animator) {
+ onCancel?.invoke(animator)
+ }
+
+ override fun onAnimationStart(animator: Animator) {
+ onStart?.invoke(animator)
+ }
+ }
+ addListener(listener)
+ return listener
+}
+
+/**
+ * Add a pause and resume listener to this Animator using the provided actions.
+ */
+@RequiresApi(19)
+fun Animator.addPauseListener(
+ onResume: ((animator: Animator) -> Unit)? = null,
+ onPause: ((animator: Animator) -> Unit)? = null
+): Animator.AnimatorPauseListener {
+ val listener = object : Animator.AnimatorPauseListener {
+ override fun onAnimationPause(animator: Animator) {
+ onPause?.invoke(animator)
+ }
+
+ override fun onAnimationResume(animator: Animator) {
+ onResume?.invoke(animator)
+ }
+ }
+ addPauseListener(listener)
+ return listener
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/ContentValues.kt b/core/ktx/src/main/java/androidx/core/content/ContentValues.kt
new file mode 100644
index 0000000..e1a0d01
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/ContentValues.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.content
+
+import android.content.ContentValues
+
+/**
+ * Returns a new [ContentValues] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [ContentValues].
+ */
+fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply {
+ for ((key, value) in pairs) {
+ when (value) {
+ null -> putNull(key)
+ is String -> put(key, value)
+ is Int -> put(key, value)
+ is Long -> put(key, value)
+ is Boolean -> put(key, value)
+ is Float -> put(key, value)
+ is Double -> put(key, value)
+ is ByteArray -> put(key, value)
+ is Byte -> put(key, value)
+ is Short -> put(key, value)
+ else -> {
+ val valueType = value.javaClass.canonicalName
+ throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+ }
+ }
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/Context.kt b/core/ktx/src/main/java/androidx/core/content/Context.kt
new file mode 100644
index 0000000..2ffb17b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/Context.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.content
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import androidx.annotation.AttrRes
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleRes
+
+/**
+ * Return the handle to a system-level service by class.
+ *
+ * The return type of this function intentionally uses a
+ * [platform type](https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types)
+ * to allow callers to decide whether they require a service be present or can tolerate its absence.
+ *
+ * @see Context.getSystemService(Class)
+ */
+@RequiresApi(23)
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline fun <reified T> Context.systemService() = getSystemService(T::class.java)
+
+/**
+ * Executes [block] on a [TypedArray] receiver. The [TypedArray] holds the attribute
+ * values in [set] that are listed in [attrs]. In addition, if the given [AttributeSet]
+ * specifies a style class (through the `style` attribute), that style will be applied
+ * on top of the base attributes it defines.
+ *
+ * @param set The base set of attribute values.
+ * @param attrs The desired attributes to be retrieved. These attribute IDs must be
+ * sorted in ascending order.
+ * @param defStyleAttr An attribute in the current theme that contains a reference to
+ * a style resource that supplies defaults values for the [TypedArray].
+ * Can be 0 to not look for defaults.
+ * @param defStyleRes A resource identifier of a style resource that supplies default values
+ * for the [TypedArray], used only if [defStyleAttr] is 0 or can not be found
+ * in the theme. Can be 0 to not look for defaults.
+ *
+ * @see Context.obtainStyledAttributes
+ * @see android.content.res.Resources.Theme.obtainStyledAttributes
+ */
+inline fun Context.withStyledAttributes(
+ set: AttributeSet? = null,
+ attrs: IntArray,
+ @AttrRes defStyleAttr: Int = 0,
+ @StyleRes defStyleRes: Int = 0,
+ block: TypedArray.() -> Unit
+) {
+ val typedArray = obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes)
+ try {
+ typedArray.block()
+ } finally {
+ typedArray.recycle()
+ }
+}
+
+/**
+ * Executes [block] on a [TypedArray] receiver. The [TypedArray] holds the the values
+ * defined by the style resource [resourceId] which are listed in [attrs].
+ *
+ * @param attrs The desired attributes. These attribute IDs must be sorted in ascending order.
+ *
+ * @see Context.obtainStyledAttributes
+ * @see android.content.res.Resources.Theme.obtainStyledAttributes
+ */
+inline fun Context.withStyledAttributes(
+ @StyleRes resourceId: Int,
+ attrs: IntArray,
+ block: TypedArray.() -> Unit
+) {
+ val typedArray = obtainStyledAttributes(resourceId, attrs)
+ try {
+ typedArray.block()
+ } finally {
+ typedArray.recycle()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt b/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt
new file mode 100644
index 0000000..c4677ea
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.content
+
+import android.annotation.SuppressLint
+import android.content.SharedPreferences
+
+/**
+ * Allows editing of this preference instance with a call to [apply][SharedPreferences.Editor.apply]
+ * or [commit][SharedPreferences.Editor.commit] to persist the changes.
+ * Default behaviour is [apply][SharedPreferences.Editor.apply].
+ * ```
+ * prefs.edit {
+ * putString("key", value)
+ * }
+ * ```
+ * To [commit][SharedPreferences.Editor.commit] changes:
+ * ```
+ * prefs.edit(commit = true) {
+ * putString("key", value)
+ * }
+ * ```
+ */
+@SuppressLint("ApplySharedPref")
+inline fun SharedPreferences.edit(
+ commit: Boolean = false,
+ action: SharedPreferences.Editor.() -> Unit
+) {
+ val editor = edit()
+ action(editor)
+ if (commit) {
+ editor.commit()
+ } else {
+ editor.apply()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt b/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt
new file mode 100644
index 0000000..a4f6fef
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.content.res
+
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.Typeface
+import android.graphics.drawable.Drawable
+import androidx.annotation.AnyRes
+import androidx.annotation.ColorInt
+import androidx.annotation.Dimension
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleableRes
+
+private fun TypedArray.checkAttribute(@StyleableRes index: Int) {
+ if (!hasValue(index)) {
+ throw IllegalArgumentException("Attribute not defined in set.")
+ }
+}
+
+/**
+ * Retrieve the boolean value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getBoolean
+ */
+fun TypedArray.getBooleanOrThrow(@StyleableRes index: Int): Boolean {
+ checkAttribute(index)
+ return getBoolean(index, false)
+}
+
+/**
+ * Retrieve the color value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getColor
+ */
+@ColorInt
+fun TypedArray.getColorOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getColor(index, 0)
+}
+
+/**
+ * Retrieve the color state list value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getColorStateList
+ */
+fun TypedArray.getColorStateListOrThrow(@StyleableRes index: Int): ColorStateList {
+ checkAttribute(index)
+ return checkNotNull(getColorStateList(index)) {
+ "Attribute value was not a color or color state list."
+ }
+}
+
+/**
+ * Retrieve the dimension value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimension
+ */
+fun TypedArray.getDimensionOrThrow(@StyleableRes index: Int): Float {
+ checkAttribute(index)
+ return getDimension(index, 0f)
+}
+
+/**
+ * Retrieve the dimension pixel offset value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimensionPixelOffset
+ */
+@Dimension
+fun TypedArray.getDimensionPixelOffsetOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getDimensionPixelOffset(index, 0)
+}
+
+/**
+ * Retrieve the dimension pixel size value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimensionPixelSize
+ */
+@Dimension
+fun TypedArray.getDimensionPixelSizeOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getDimensionPixelSize(index, 0)
+}
+
+/**
+ * Retrieve the drawable value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDrawable
+ */
+fun TypedArray.getDrawableOrThrow(@StyleableRes index: Int): Drawable {
+ checkAttribute(index)
+ return getDrawable(index)
+}
+
+/**
+ * Retrieve the float value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getFloat
+ */
+fun TypedArray.getFloatOrThrow(@StyleableRes index: Int): Float {
+ checkAttribute(index)
+ return getFloat(index, 0f)
+}
+
+/**
+ * Retrieve the font value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getFont
+ */
+@RequiresApi(26)
+fun TypedArray.getFontOrThrow(@StyleableRes index: Int): Typeface {
+ checkAttribute(index)
+ return getFont(index)
+}
+
+/**
+ * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getInt
+ */
+fun TypedArray.getIntOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getInt(index, 0)
+}
+
+/**
+ * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getInteger
+ */
+fun TypedArray.getIntegerOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getInteger(index, 0)
+}
+
+/**
+ * Retrieves the resource identifier for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getResourceId
+ */
+@AnyRes
+fun TypedArray.getResourceIdOrThrow(@StyleableRes index: Int): Int {
+ checkAttribute(index)
+ return getResourceId(index, 0)
+}
+
+/**
+ * Retrieve the string value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getString
+ */
+fun TypedArray.getStringOrThrow(@StyleableRes index: Int): String {
+ checkAttribute(index)
+ return checkNotNull(getString(index)) {
+ "Attribute value could not be coerced to String."
+ }
+}
+
+/**
+ * Retrieve the text value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getText
+ */
+fun TypedArray.getTextOrThrow(@StyleableRes index: Int): CharSequence {
+ checkAttribute(index)
+ return checkNotNull(getText(index)) {
+ "Attribute value could not be coerced to CharSequence."
+ }
+}
+
+/**
+ * Retrieve the text array value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getTextArray
+ */
+fun TypedArray.getTextArrayOrThrow(@StyleableRes index: Int): Array<CharSequence> {
+ checkAttribute(index)
+ return getTextArray(index)
+}
+
+/**
+ * Executes the given [block] function on this TypedArray and then recycles it.
+ *
+ * @see kotlin.io.use
+ */
+inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
+ return block(this).also {
+ recycle()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/database/Cursor.kt b/core/ktx/src/main/java/androidx/core/database/Cursor.kt
new file mode 100644
index 0000000..b63e719
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/database/Cursor.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.database
+
+import android.database.Cursor
+
+/**
+ * Returns the value of the requested column as a byte array.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a blob type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlob(columnName: String): ByteArray =
+ getBlob(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a double.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDouble(columnName: String): Double =
+ getDouble(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a float.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloat(columnName: String): Float = getFloat(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as an integer.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getInt(columnName: String): Int = getInt(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a long.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLong(columnName: String): Long = getLong(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a short.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShort(columnName: String): Short = getShort(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a string.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a string type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getString
+ */
+inline fun Cursor.getString(columnName: String): String =
+ getString(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a nullable byte array.
+ *
+ * The result and whether this method throws an exception when the column type is not a blob type is
+ * implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlobOrNull(index: Int) = if (isNull(index)) null else getBlob(index)
+
+/**
+ * Returns the value of the requested column as a nullable double.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDoubleOrNull(index: Int) = if (isNull(index)) null else getDouble(index)
+
+/**
+ * Returns the value of the requested column as a nullable float.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloatOrNull(index: Int) = if (isNull(index)) null else getFloat(index)
+
+/**
+ * Returns the value of the requested column as a nullable integer.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getIntOrNull(index: Int) = if (isNull(index)) null else getInt(index)
+
+/**
+ * Returns the value of the requested column as a nullable long.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLongOrNull(index: Int) = if (isNull(index)) null else getLong(index)
+
+/**
+ * Returns the value of the requested column as a nullable short.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShortOrNull(index: Int) = if (isNull(index)) null else getShort(index)
+
+/**
+ * Returns the value of the requested column as a nullable string.
+ *
+ * The result and whether this method throws an exception when the column type is not a string type
+ * is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getString
+ */
+inline fun Cursor.getStringOrNull(index: Int) = if (isNull(index)) null else getString(index)
+
+/**
+ * Returns the value of the requested column as a nullable byte array.
+ *
+ * The result and whether this method throws an exception when the column type is not a blob type is
+ * implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlobOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getBlob(it) }
+
+/**
+ * Returns the value of the requested column as a nullable double.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDoubleOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getDouble(it) }
+
+/**
+ * Returns the value of the requested column as a nullable float.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloatOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getFloat(it) }
+
+/**
+ * Returns the value of the requested column as a nullable integer.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getIntOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getInt(it) }
+
+/**
+ * Returns the value of the requested column as a nullable long.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLongOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getLong(it) }
+
+/**
+ * Returns the value of the requested column as a nullable short.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShortOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getShort(it) }
+
+/**
+ * Returns the value of the requested column as a nullable string.
+ *
+ * The result and whether this method throws an exception when the column type is not a string type
+ * is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getString
+ */
+inline fun Cursor.getStringOrNull(columnName: String) =
+ getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getString(it) }
diff --git a/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt b/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt
new file mode 100644
index 0000000..bdbd94e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.database.sqlite
+
+import android.database.sqlite.SQLiteDatabase
+
+/**
+ * Run [body] in a transaction marking it as successful if it completes without exception.
+ *
+ * @param exclusive Run in `EXCLUSIVE` mode when true, `IMMEDIATE` mode otherwise.
+ */
+inline fun <T> SQLiteDatabase.transaction(
+ exclusive: Boolean = true,
+ body: SQLiteDatabase.() -> T
+): T {
+ if (exclusive) {
+ beginTransaction()
+ } else {
+ beginTransactionNonExclusive()
+ }
+ try {
+ val result = body()
+ setTransactionSuccessful()
+ return result
+ } finally {
+ endTransaction()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt b/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt
new file mode 100644
index 0000000..45ab6ba
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorSpace
+import androidx.annotation.ColorInt
+import androidx.annotation.RequiresApi
+
+/**
+ * Creates a new [Canvas] to draw on this bitmap and executes the specified
+ * [block] on the newly created canvas. Example:
+ *
+ * ```
+ * return Bitmap.createBitmap(…).applyCanvas {
+ * drawLine(…)
+ * translate(…)
+ * drawRect(…)
+ * }
+ * ```
+ */
+inline fun Bitmap.applyCanvas(block: Canvas.() -> Unit): Bitmap {
+ val c = Canvas(this)
+ c.block()
+ return this
+}
+
+/**
+ * Returns the value of the pixel at the specified location. The returned value
+ * is a [color int][android.graphics.Color] in the sRGB color space.
+ */
+inline operator fun Bitmap.get(x: Int, y: Int) = getPixel(x, y)
+
+/**
+ * Writes the specified [color int][android.graphics.Color] into the bitmap
+ * (assuming it is mutable) at the specified `(x, y)` coordinate. The specified
+ * color is converted from sRGB to the bitmap's color space if needed.
+ */
+inline operator fun Bitmap.set(x: Int, y: Int, @ColorInt color: Int) = setPixel(x, y, color)
+
+/**
+ * Creates a new bitmap, scaled from this bitmap, when possible. If the specified
+ * [width] and [height] are the same as the current width and height of this bitmap,
+ * this bitmap is returned and no new bitmap is created.
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param filter `true` if the source should be filtered (`true` by default)
+ *
+ * @return The new scaled bitmap or the source bitmap if no scaling is required.
+ */
+inline fun Bitmap.scale(width: Int, height: Int, filter: Boolean = true): Bitmap {
+ return Bitmap.createScaledBitmap(this, width, height, filter)
+}
+
+/**
+ * Returns a mutable bitmap with the specified [width] and [height]. A config
+ * can be optionally specified. If not, the default config is [Bitmap.Config.ARGB_8888].
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param config The new bitmap's desired [config][Bitmap.Config]
+ *
+ * @return A new bitmap with the specified dimensions and config
+ */
+inline fun createBitmap(
+ width: Int,
+ height: Int,
+ config: Bitmap.Config = Bitmap.Config.ARGB_8888
+): Bitmap {
+ return Bitmap.createBitmap(width, height, config)
+}
+
+/**
+ * Returns a mutable bitmap with the specified [width] and [height]. The config,
+ * transparency and color space can optionally be specified. They respectively
+ * default to [Bitmap.Config.ARGB_8888], `true` and [sRGB][ColorSpace.Named.SRGB].
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param config The new bitmap's desired [config][Bitmap.Config]
+ * @param hasAlpha Whether the new bitmap is opaque or not
+ * @param colorSpace The new bitmap's color space
+ *
+ * @return A new bitmap with the specified dimensions and config
+ */
+@RequiresApi(26)
+inline fun createBitmap(
+ width: Int,
+ height: Int,
+ config: Bitmap.Config = Bitmap.Config.ARGB_8888,
+ hasAlpha: Boolean = true,
+ colorSpace: ColorSpace = ColorSpace.get(ColorSpace.Named.SRGB)
+): Bitmap {
+ return Bitmap.createBitmap(width, height, config, hasAlpha, colorSpace)
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt b/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt
new file mode 100644
index 0000000..71efc08
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Matrix
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withSave(block: Canvas.() -> Unit) {
+ val checkpoint = save()
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.translate]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withTranslation(
+ x: Float = 0.0f,
+ y: Float = 0.0f,
+ block: Canvas.() -> Unit
+) {
+ val checkpoint = save()
+ translate(x, y)
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.rotate]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withRotation(
+ degrees: Float = 0.0f,
+ pivotX: Float = 0.0f,
+ pivotY: Float = 0.0f,
+ block: Canvas.() -> Unit
+) {
+ val checkpoint = save()
+ rotate(degrees, pivotX, pivotY)
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.scale]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withScale(
+ x: Float = 1.0f,
+ y: Float = 1.0f,
+ pivotX: Float = 0.0f,
+ pivotY: Float = 0.0f,
+ block: Canvas.() -> Unit
+) {
+ val checkpoint = save()
+ scale(x, y, pivotX, pivotY)
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.skew]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withSkew(
+ x: Float = 0.0f,
+ y: Float = 0.0f,
+ block: Canvas.() -> Unit
+) {
+ val checkpoint = save()
+ skew(x, y)
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.concat]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withMatrix(
+ matrix: Matrix = Matrix(),
+ block: Canvas.() -> Unit
+) {
+ val checkpoint = save()
+ concat(matrix)
+ try {
+ block()
+ } finally {
+ restoreToCount(checkpoint)
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Color.kt b/core/ktx/src/main/java/androidx/core/graphics/Color.kt
new file mode 100644
index 0000000..f853422
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Color.kt
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics
+
+import android.graphics.Color
+import android.graphics.ColorSpace
+import androidx.annotation.ColorInt
+import androidx.annotation.ColorLong
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns the first component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the first component
+ * is "red".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component1() = getComponent(0)
+
+/**
+ * Returns the second component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the second component
+ * is "green".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component2() = getComponent(1)
+
+/**
+ * Returns the third component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the third component
+ * is "blue".
+= *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component3() = getComponent(2)
+
+/**
+ * Returns the fourth component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the fourth component
+ * is "alpha".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component4() = getComponent(3)
+
+/**
+ * Composites two translucent colors together. More specifically, adds two colors using
+ * the [source over][android.graphics.PorterDuff.Mode.SRC_OVER] blending mode. The colors
+ * must not be pre-multiplied and the result is a non pre-multiplied color.
+ *
+ * If the two colors have different color spaces, the color in the right-hand part
+ * of the expression is converted to the color space of the color in left-hand part
+ * of the expression.
+ *
+ * The following example creates a purple color by blending opaque blue with
+ * semi-translucent red:
+ *
+ * ```
+ * val purple = Color.valueOf(0f, 0f, 1f) + Color.valueOf(1f, 0f, 0f, 0.5f)
+ * ```
+ *
+ * @throws IllegalArgumentException if the [color models][android.graphics.Color.getModel]
+ * of the colors do not match
+ */
+@RequiresApi(26)
+operator fun Color.plus(c: Color): Color = ColorUtils.compositeColors(c, this)
+
+/**
+ * Return the alpha component of a color int. This is equivalent to calling:
+ * ```
+ * Color.alpha(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.alpha get() = (this shr 24) and 0xff
+
+/**
+ * Return the red component of a color int. This is equivalent to calling:
+ * ```
+ * Color.red(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.red get() = (this shr 16) and 0xff
+
+/**
+ * Return the green component of a color int. This is equivalent to calling:
+ * ```
+ * Color.green(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.green get() = (this shr 8) and 0xff
+
+/**
+ * Return the blue component of a color int. This is equivalent to calling:
+ * ```
+ * Color.blue(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.blue get() = this and 0xff
+
+/**
+ * Return the alpha component of a color int. This is equivalent to calling:
+ * ```
+ * Color.alpha(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component1() = (this shr 24) and 0xff
+
+/**
+ * Return the red component of a color int. This is equivalent to calling:
+ * ```
+ * Color.red(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component2() = (this shr 16) and 0xff
+
+/**
+ * Return the green component of a color int. This is equivalent to calling:
+ * ```
+ * Color.green(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component3() = (this shr 8) and 0xff
+
+/**
+ * Return the blue component of a color int. This is equivalent to calling:
+ * ```
+ * Color.blue(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component4() = this and 0xff
+
+/**
+ * Returns the relative luminance of a color int, assuming sRGB encoding.
+ * Based on the formula for relative luminance defined in WCAG 2.0,
+ * W3C Recommendation 11 December 2008.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorInt Int.luminance get() = Color.luminance(this)
+
+/**
+ * Creates a new [Color] instance from a color int. The resulting color
+ * is in the [sRGB][android.graphics.ColorSpace.Named.SRGB] color space.
+ */
+@RequiresApi(26)
+inline fun @receiver:ColorInt Int.toColor(): Color = Color.valueOf(this)
+
+/**
+ * Converts the specified ARGB [color int][Color] to an RGBA [color long][Color]
+ * in the [sRGB][android.graphics.ColorSpace.Named.SRGB] color space.
+ */
+@RequiresApi(26)
+@ColorLong
+inline fun @receiver:ColorInt Int.toColorLong() = Color.pack(this)
+
+/**
+ * Returns the first component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the first component
+ * is "red".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component1() = Color.red(this)
+
+/**
+ * Returns the second component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the second component
+ * is "green".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component2() = Color.green(this)
+
+/**
+ * Returns the third component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the third component
+ * is "blue".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component3() = Color.blue(this)
+
+/**
+ * Returns the fourth component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the fourth component
+ * is "alpha".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component4() = Color.alpha(this)
+
+/**
+ * Return the alpha component of a color long. This is equivalent to calling:
+ * ```
+ * Color.alpha(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.alpha get() = Color.alpha(this)
+
+/**
+ * Return the red component of a color long. This is equivalent to calling:
+ * ```
+ * Color.red(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.red get() = Color.red(this)
+
+/**
+ * Return the green component of a color long. This is equivalent to calling:
+ * ```
+ * Color.green(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.green get() = Color.green(this)
+
+/**
+ * Return the blue component of a color long. This is equivalent to calling:
+ * ```
+ * Color.blue(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.blue get() = Color.blue(this)
+
+/**
+ * Returns the relative luminance of a color. Based on the formula for
+ * relative luminance defined in WCAG 2.0, W3C Recommendation 11 December 2008.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.luminance get() = Color.luminance(this)
+
+/**
+ * Creates a new [Color] instance from a [color long][Color].
+ */
+@RequiresApi(26)
+inline fun @receiver:ColorLong Long.toColor(): Color = Color.valueOf(this)
+
+/**
+ * Converts the specified [color long][Color] to an ARGB [color int][Color].
+ */
+@RequiresApi(26)
+@ColorInt
+inline fun @receiver:ColorLong Long.toColorInt() = Color.toArgb(this)
+
+/**
+ * Indicates whether the color is in the [sRGB][android.graphics.ColorSpace.Named.SRGB]
+ * color space.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.isSrgb get() = Color.isSrgb(this)
+
+/**
+ * Indicates whether the color is in a [wide-gamut][android.graphics.ColorSpace] color space.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.isWideGamut get() = Color.isWideGamut(this)
+
+/**
+ * Returns the color space encoded in the specified color long.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.colorSpace: ColorSpace get() = Color.colorSpace(this)
+
+/**
+ * Return a corresponding [Int] color of this [String].
+ *
+ * Supported formats are:
+ * ```
+ * #RRGGBB
+ * #AARRGGBB
+ * ```
+ *
+ * The following names are also accepted: "red", "blue", "green", "black", "white",
+ * "gray", "cyan", "magenta", "yellow", "lightgray", "darkgray",
+ * "grey", "lightgrey", "darkgrey", "aqua", "fuchsia", "lime",
+ * "maroon", "navy", "olive", "purple", "silver", "teal".
+ *
+ * @throws IllegalArgumentException if this [String] cannot be parsed.
+ */
+@ColorInt
+inline fun String.toColorInt(): Int = Color.parseColor(this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt b/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt
new file mode 100644
index 0000000..5cb0cdf
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Matrix
+
+/**
+ * Multiplies this [Matrix] by another matrix and returns the result as
+ * a new matrix.
+ */
+inline operator fun Matrix.times(m: Matrix) = Matrix(this).apply { preConcat(m) }
+
+/**
+ * Returns the 9 values of this [Matrix] as a new array of floats.
+ */
+inline fun Matrix.values() = FloatArray(9).apply { getValues(this) }
+
+/**
+ * Creates a translation matrix with the translation amounts [tx] and [ty]
+ * respectively on the `x` and `y` axis.
+ */
+fun translationMatrix(tx: Float = 0.0f, ty: Float = 0.0f) = Matrix().apply { setTranslate(tx, ty) }
+
+/**
+ * Creates a scale matrix with the scale factor [sx] and [sy] respectively on the
+ * `x` and `y` axis.
+ */
+fun scaleMatrix(sx: Float = 1.0f, sy: Float = 1.0f) = Matrix().apply { setScale(sx, sy) }
+
+/**
+ * Creates a rotation matrix, defined by a rotation angle in degrees around the pivot
+ * point located at the coordinates ([px], [py]).
+ */
+fun rotationMatrix(degrees: Float, px: Float = 0.0f, py: Float = 0.0f) =
+ Matrix().apply { setRotate(degrees, px, py) }
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Path.kt b/core/ktx/src/main/java/androidx/core/graphics/Path.kt
new file mode 100644
index 0000000..ca70533
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Path.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Path
+import androidx.annotation.RequiresApi
+
+/**
+ * Flattens (or approximate) the [Path] with a series of line segments.
+ *
+ * @param error The acceptable error for a line on the Path. Typically this would be
+ * 0.5 so that the error is less than half a pixel. This value must be
+ * positive and is set to 0.5 by default.
+ *
+ * @see Path.approximate
+ */
+@RequiresApi(26)
+fun Path.flatten(error: Float = 0.5f): Iterable<PathSegment> = PathUtils.flatten(this, error)
+
+/**
+ * Returns the union of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline operator fun Path.plus(p: Path): Path {
+ return Path(this).apply {
+ op(p, Path.Op.UNION)
+ }
+}
+
+/**
+ * Returns the difference of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline operator fun Path.minus(p: Path): Path {
+ return Path(this).apply {
+ op(p, Path.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Returns the union of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline infix fun Path.and(p: Path) = this + p
+
+/**
+ * Returns the intersection of two paths as a new [Path].
+ * If the paths do not intersect, returns an empty path.
+ */
+@RequiresApi(19)
+inline infix fun Path.or(p: Path): Path {
+ return Path().apply {
+ op(this@or, p, Path.Op.INTERSECT)
+ }
+}
+
+/**
+ * Returns the union minus the intersection of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline infix fun Path.xor(p: Path): Path {
+ return Path(this).apply {
+ op(p, Path.Op.XOR)
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Picture.kt b/core/ktx/src/main/java/androidx/core/graphics/Picture.kt
new file mode 100644
index 0000000..d1e8c99
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Picture.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 20188 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Picture
+
+/**
+ * Creates a new [Canvas] to record commands in this [Picture], executes the specified
+ * [block] on the newly created canvas and returns this [Picture]. Example:
+ *
+ * ```
+ * return myPicture.record(1280, 720) {
+ * drawLine(…)
+ * translate(…)
+ * drawRect(…)
+ * }
+ * ```
+ */
+inline fun Picture.record(width: Int, height: Int, block: Canvas.() -> Unit): Picture {
+ val c = beginRecording(width, height)
+ try {
+ c.block()
+ } finally {
+ endRecording()
+ }
+ return this
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Point.kt b/core/ktx/src/main/java/androidx/core/graphics/Point.kt
new file mode 100644
index 0000000..b6e35e6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Point.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.PointF
+
+/**
+ * Returns the x coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun Point.component1() = this.x
+
+/**
+ * Returns the y coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun Point.component2() = this.y
+
+/**
+ * Returns the x coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun PointF.component1() = this.x
+
+/**
+ * Returns the y coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun PointF.component2() = this.y
+
+/**
+ * Offsets this point by the specified point and returns the result as a new point.
+ */
+inline operator fun Point.plus(p: Point): Point {
+ return Point(x, y).apply {
+ offset(p.x, p.y)
+ }
+}
+
+/**
+ * Offsets this point by the specified point and returns the result as a new point.
+ */
+inline operator fun PointF.plus(p: PointF): PointF {
+ return PointF(x, y).apply {
+ offset(p.x, p.y)
+ }
+}
+
+/**
+ * Offsets this point by the specified amount on both X and Y axis and returns the
+ * result as a new point.
+ */
+inline operator fun Point.plus(xy: Int): Point {
+ return Point(x, y).apply {
+ offset(xy, xy)
+ }
+}
+
+/**
+ * Offsets this point by the specified amount on both X and Y axis and returns the
+ * result as a new point.
+ */
+inline operator fun PointF.plus(xy: Float): PointF {
+ return PointF(x, y).apply {
+ offset(xy, xy)
+ }
+}
+
+/**
+ * Offsets this point by the negation of the specified point and returns the result
+ * as a new point.
+ */
+inline operator fun Point.minus(p: Point): Point {
+ return Point(x, y).apply {
+ offset(-p.x, -p.y)
+ }
+}
+
+/**
+ * Offsets this point by the negation of the specified point and returns the result
+ * as a new point.
+ */
+inline operator fun PointF.minus(p: PointF): PointF {
+ return PointF(x, y).apply {
+ offset(-p.x, -p.y)
+ }
+}
+
+/**
+ * Offsets this point by the negation of the specified amount on both X and Y axis and
+ * returns the result as a new point.
+ */
+inline operator fun Point.minus(xy: Int): Point {
+ return Point(x, y).apply {
+ offset(-xy, -xy)
+ }
+}
+
+/**
+ * Offsets this point by the negation of the specified amount on both X and Y axis and
+ * returns the result as a new point.
+ */
+inline operator fun PointF.minus(xy: Float): PointF {
+ return PointF(x, y).apply {
+ offset(-xy, -xy)
+ }
+}
+
+/**
+ * Returns a new point representing the negation of this point.
+ */
+inline operator fun Point.unaryMinus() = Point(-x, -y)
+
+/**
+ * Returns a new point representing the negation of this point.
+ */
+inline operator fun PointF.unaryMinus() = PointF(-x, -y)
+
+/**
+ * Returns a [PointF] representation of this point.
+ */
+inline fun Point.toPointF() = PointF(this)
+
+/**
+ * Returns a [Point] representation of this point.
+ */
+inline fun PointF.toPoint() = Point(x.toInt(), y.toInt())
diff --git a/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt b/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt
new file mode 100644
index 0000000..a34d6f9
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.PorterDuffXfermode
+
+/**
+ * Creates a new [PorterDuffXfermode] that uses this [PorterDuff.Mode] as the
+ * alpha compositing or blending mode.
+ */
+inline fun PorterDuff.Mode.toXfermode() = PorterDuffXfermode(this)
+
+/**
+ * Creates a new [PorterDuffColorFilter] that uses this [PorterDuff.Mode] as the
+ * alpha compositing or blending mode, and the specified [color].
+ */
+inline fun PorterDuff.Mode.toColorFilter(color: Int) = PorterDuffColorFilter(color, this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Rect.kt b/core/ktx/src/main/java/androidx/core/graphics/Rect.kt
new file mode 100644
index 0000000..960c0c5
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Rect.kt
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.annotation.SuppressLint
+import android.graphics.Matrix
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+
+/**
+ * Returns "left", the first component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component1() = this.left
+
+/**
+ * Returns "top", the second component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component2() = this.top
+
+/**
+ * Returns "right", the third component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component3() = this.right
+
+/**
+ * Returns "bottom", the fourth component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component4() = this.bottom
+
+/**
+ * Returns "left", the first component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component1() = this.left
+
+/**
+ * Returns "top", the second component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component2() = this.top
+
+/**
+ * Returns "right", the third component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component3() = this.right
+
+/**
+ * Returns "bottom", the fourth component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component4() = this.bottom
+
+/**
+ * Performs the union of this rectangle and the specified rectangle and returns
+ * the result as a new rectangle.
+ */
+inline operator fun Rect.plus(r: Rect): Rect {
+ return Rect(this).apply {
+ union(r)
+ }
+}
+
+/**
+ * Performs the union of this rectangle and the specified rectangle and returns
+ * the result as a new rectangle.
+ */
+inline operator fun RectF.plus(r: RectF): RectF {
+ return RectF(this).apply {
+ union(r)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * amount on both X and Y axis.
+ */
+inline operator fun Rect.plus(xy: Int): Rect {
+ return Rect(this).apply {
+ offset(xy, xy)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * amount on both X and Y axis.
+ */
+inline operator fun RectF.plus(xy: Float): RectF {
+ return RectF(this).apply {
+ offset(xy, xy)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * point.
+ */
+inline operator fun Rect.plus(xy: Point): Rect {
+ return Rect(this).apply {
+ offset(xy.x, xy.y)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * point.
+ */
+inline operator fun RectF.plus(xy: PointF): RectF {
+ return RectF(this).apply {
+ offset(xy.x, xy.y)
+ }
+}
+
+/**
+ * Returns the difference of this rectangle and the specified rectangle as a new region.
+ */
+inline operator fun Rect.minus(r: Rect): Region {
+ return Region(this).apply {
+ op(r, Region.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Returns the difference of this rectangle and the specified rectangle as a new region.
+ * This rectangle is first converted to a [Rect] using [RectF.toRect].
+ */
+inline operator fun RectF.minus(r: RectF): Region {
+ return Region(this.toRect()).apply {
+ op(r.toRect(), Region.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation
+ * of the specified amount on both X and Y axis.
+ */
+inline operator fun Rect.minus(xy: Int): Rect {
+ return Rect(this).apply {
+ offset(-xy, -xy)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation
+ * of the specified amount on both X and Y axis.
+ */
+inline operator fun RectF.minus(xy: Float): RectF {
+ return RectF(this).apply {
+ offset(-xy, -xy)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation of
+ * the specified point.
+ */
+inline operator fun Rect.minus(xy: Point): Rect {
+ return Rect(this).apply {
+ offset(-xy.x, -xy.y)
+ }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation of
+ * the specified point.
+ */
+inline operator fun RectF.minus(xy: PointF): RectF {
+ return RectF(this).apply {
+ offset(-xy.x, -xy.y)
+ }
+}
+
+/**
+ * Returns the union of two rectangles as a new rectangle.
+ */
+inline infix fun Rect.and(r: Rect) = this + r
+
+/**
+ * Returns the union of two rectangles as a new rectangle.
+ */
+inline infix fun RectF.and(r: RectF) = this + r
+
+/**
+ * Returns the intersection of two rectangles as a new rectangle.
+ * If the rectangles do not intersect, returns a copy of the left hand side
+ * rectangle.
+ */
+@SuppressLint("CheckResult")
+inline infix fun Rect.or(r: Rect): Rect {
+ return Rect(this).apply {
+ intersect(r)
+ }
+}
+
+/**
+ * Returns the intersection of two rectangles as a new rectangle.
+ * If the rectangles do not intersect, returns a copy of the left hand side
+ * rectangle.
+ */
+@SuppressLint("CheckResult")
+inline infix fun RectF.or(r: RectF): RectF {
+ return RectF(this).apply {
+ intersect(r)
+ }
+}
+
+/**
+ * Returns the union minus the intersection of two rectangles as a new region.
+ */
+inline infix fun Rect.xor(r: Rect): Region {
+ return Region(this).apply {
+ op(r, Region.Op.XOR)
+ }
+}
+
+/**
+ * Returns the union minus the intersection of two rectangles as a new region.
+ * The two rectangles are first converted to [Rect] using [RectF.toRect].
+ */
+inline infix fun RectF.xor(r: RectF): Region {
+ return Region(this.toRect()).apply {
+ op(r.toRect(), Region.Op.XOR)
+ }
+}
+
+/**
+ * Returns true if the specified point is inside the rectangle.
+ * The left and top are considered to be inside, while the right and bottom are not.
+ * This means that for a point to be contained: left <= x < right and top <= y < bottom.
+ * An empty rectangle never contains any point.
+ */
+inline operator fun Rect.contains(p: Point) = contains(p.x, p.y)
+
+/**
+ * Returns true if the specified point is inside the rectangle.
+ * The left and top are considered to be inside, while the right and bottom are not.
+ * This means that for a point to be contained: left <= x < right and top <= y < bottom.
+ * An empty rectangle never contains any point.
+ */
+inline operator fun RectF.contains(p: PointF) = contains(p.x, p.y)
+
+/**
+ * Returns a [RectF] representation of this rectangle.
+ */
+inline fun Rect.toRectF(): RectF = RectF(this)
+
+/**
+ * Returns a [Rect] representation of this rectangle. The resulting rect will be sized such
+ * that this rect can fit within it.
+ */
+inline fun RectF.toRect(): Rect {
+ val r = Rect()
+ roundOut(r)
+ return r
+}
+
+/**
+ * Returns a [Region] representation of this rectangle.
+ */
+inline fun Rect.toRegion() = Region(this)
+
+/**
+ * Returns a [Region] representation of this rectangle. The resulting rect will be sized such
+ * that this rect can fit within it.
+ */
+inline fun RectF.toRegion() = Region(this.toRect())
+
+/**
+ * Transform this rectangle in place using the supplied [Matrix] and returns
+ * this rectangle.
+ */
+inline fun RectF.transform(m: Matrix) = apply { m.mapRect(this@transform) }
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Region.kt b/core/ktx/src/main/java/androidx/core/graphics/Region.kt
new file mode 100644
index 0000000..4fc5179
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Region.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.Region
+import android.graphics.RegionIterator
+
+/**
+ * Return true if the region contains the specified [Point].
+ */
+inline operator fun Region.contains(p: Point) = contains(p.x, p.y)
+
+/**
+ * Return the union of this region and the specified [Rect] as a new region.
+ */
+inline operator fun Region.plus(r: Rect): Region {
+ return Region(this).apply {
+ union(r)
+ }
+}
+
+/**
+ * Return the union of this region and the specified region as a new region.
+ */
+inline operator fun Region.plus(r: Region): Region {
+ return Region(this).apply {
+ op(r, Region.Op.UNION)
+ }
+}
+
+/**
+ * Return the difference of this region and the specified [Rect] as a new region.
+ */
+inline operator fun Region.minus(r: Rect): Region {
+ return Region(this).apply {
+ op(r, Region.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Return the difference of this region and the specified region as a new region.
+ */
+inline operator fun Region.minus(r: Region): Region {
+ return Region(this).apply {
+ op(r, Region.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Returns the negation of this region as a new region.
+ */
+inline operator fun Region.unaryMinus(): Region {
+ return Region(bounds).apply {
+ op(this@unaryMinus, Region.Op.DIFFERENCE)
+ }
+}
+
+/**
+ * Returns the negation of this region as a new region.
+ */
+inline operator fun Region.not() = -this
+
+/**
+ * Return the union of this region and the specified [Rect] as a new region.
+ */
+inline infix fun Region.and(r: Rect) = this + r
+
+/**
+ * Return the union of this region and the specified region as a new region.
+ */
+inline infix fun Region.and(r: Region) = this + r
+
+/**
+ * Return the intersection of this region and the specified [Rect] as a new region.
+ */
+inline infix fun Region.or(r: Rect): Region {
+ return Region(this).apply {
+ op(r, Region.Op.INTERSECT)
+ }
+}
+
+/**
+ * Return the intersection of this region and the specified region as a new region.
+ */
+inline infix fun Region.or(r: Region): Region {
+ return Region(this).apply {
+ op(r, Region.Op.INTERSECT)
+ }
+}
+
+/**
+ * Return the union minus the intersection of this region and the specified [Rect]
+ * as a new region.
+ */
+inline infix fun Region.xor(r: Rect): Region {
+ return Region(this).apply {
+ op(r, Region.Op.XOR)
+ }
+}
+
+/**
+ * Return the union minus the intersection of this region and the specified region
+ * as a new region.
+ */
+inline infix fun Region.xor(r: Region): Region {
+ return Region(this).apply {
+ op(r, Region.Op.XOR)
+ }
+}
+
+/** Performs the given action on each rect in this region. */
+inline fun Region.forEach(action: (rect: Rect) -> Unit) {
+ val iterator = RegionIterator(this)
+ while (true) {
+ val r = Rect()
+ if (!iterator.next(r)) {
+ break
+ }
+ action(r)
+ }
+}
+
+/** Returns an [Iterator] over the rects in this region. */
+operator fun Region.iterator() = object : Iterator<Rect> {
+ private val iterator = RegionIterator(this@iterator)
+ private val rect = Rect()
+ private var hasMore = iterator.next(rect)
+
+ override fun hasNext() = hasMore
+
+ override fun next(): Rect {
+ if (hasMore) {
+ val r = Rect(rect)
+ hasMore = iterator.next(rect)
+ return r
+ }
+ throw IndexOutOfBoundsException()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Shader.kt b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
new file mode 100644
index 0000000..0fb93cb
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Shader
+
+/**
+ * Wrap the specified [block] in calls to [Shader.getLocalMatrix] and [Shader.setLocalMatrix].
+ */
+inline fun Shader.transform(block: Matrix.() -> Unit) {
+ val matrix = Matrix()
+ getLocalMatrix(matrix)
+ block(matrix)
+ setLocalMatrix(matrix)
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt
new file mode 100644
index 0000000..4a96aab
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics.drawable
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+
+/** Create a [BitmapDrawable] from this [Bitmap]. */
+inline fun Bitmap.toDrawable(resources: Resources) = BitmapDrawable(resources, this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt
new file mode 100644
index 0000000..bef04df
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics.drawable
+
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import androidx.annotation.ColorInt
+import androidx.annotation.RequiresApi
+
+/** Create a [ColorDrawable] from this color value. */
+inline fun @receiver:ColorInt Int.toDrawable() = ColorDrawable(this)
+
+/** Create a [ColorDrawable] from this [Color] (via [Color.toArgb]). */
+@RequiresApi(26)
+inline fun Color.toDrawable() = ColorDrawable(toArgb())
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt
new file mode 100644
index 0000000..9e63bbd
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config
+import android.graphics.Canvas
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import androidx.annotation.Px
+import androidx.core.graphics.component1
+import androidx.core.graphics.component2
+import androidx.core.graphics.component3
+import androidx.core.graphics.component4
+
+/**
+ * Return a [Bitmap] representation of this [Drawable].
+ *
+ * If this instance is a [BitmapDrawable] and the [width], [height], and [config] match, the
+ * underlying [Bitmap] instance will be returned directly. If any of those three properties differ
+ * then a new [Bitmap] is created. For all other [Drawable] types, a new [Bitmap] is created.
+ *
+ * @param width Width of the desired bitmap. Defaults to [Drawable.getIntrinsicWidth].
+ * @param height Height of the desired bitmap. Defaults to [Drawable.getIntrinsicHeight].
+ * @param config Bitmap config of the desired bitmap. Null attempts to use the native config, if
+ * any. Defaults to [Config.ARGB_8888] otherwise.
+ */
+fun Drawable.toBitmap(
+ @Px width: Int = intrinsicWidth,
+ @Px height: Int = intrinsicHeight,
+ config: Config? = null
+): Bitmap {
+ if (this is BitmapDrawable) {
+ if (config == null || bitmap.config == config) {
+ // Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
+ // involves allocation and two jumps into native code so we perform the check ourselves.
+ if (width == intrinsicWidth && height == intrinsicHeight) {
+ return bitmap
+ }
+ return Bitmap.createScaledBitmap(bitmap, width, height, true)
+ }
+ }
+
+ val (oldLeft, oldTop, oldRight, oldBottom) = bounds
+
+ val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
+ setBounds(0, 0, width, height)
+ draw(Canvas(bitmap))
+
+ setBounds(oldLeft, oldTop, oldRight, oldBottom)
+ return bitmap
+}
+
+/**
+ * Updates this drawable's bounds. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see Drawable.setBounds
+ */
+fun Drawable.updateBounds(
+ @Px left: Int = bounds.left,
+ @Px top: Int = bounds.top,
+ @Px right: Int = bounds.right,
+ @Px bottom: Int = bounds.bottom
+) {
+ setBounds(left, top, right, bottom)
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt
new file mode 100644
index 0000000..a9077e2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.net.Uri
+import androidx.annotation.RequiresApi
+
+/**
+ * Create an [Icon] from this adaptive [Bitmap].
+ *
+ * @see Icon.createWithAdaptiveBitmap
+ */
+@RequiresApi(26)
+inline fun Bitmap.toAdaptiveIcon(): Icon = Icon.createWithAdaptiveBitmap(this)
+
+/**
+ * Create an [Icon] from this [Bitmap].
+ *
+ * @see Icon.createWithBitmap
+ */
+@RequiresApi(26)
+inline fun Bitmap.toIcon(): Icon = Icon.createWithBitmap(this)
+
+/**
+ * Create an [Icon] from this [Uri].
+ *
+ * @see Icon.createWithContentUri
+ */
+@RequiresApi(26)
+inline fun Uri.toIcon(): Icon = Icon.createWithContentUri(this)
+
+/**
+ * Create an [Icon] from this [ByteArray].
+ *
+ * @see Icon.createWithData
+ */
+@RequiresApi(26)
+inline fun ByteArray.toIcon(): Icon = Icon.createWithData(this, 0, size)
diff --git a/core/ktx/src/main/java/androidx/core/net/Uri.kt b/core/ktx/src/main/java/androidx/core/net/Uri.kt
new file mode 100644
index 0000000..a5d9f1d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/net/Uri.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.net
+
+import android.net.Uri
+import java.io.File
+
+/**
+ * Creates a Uri from the given encoded URI string.
+ *
+ * @see Uri.parse
+ */
+inline fun String.toUri(): Uri = Uri.parse(this)
+
+/**
+ * Creates a Uri from the given file.
+ *
+ * @see Uri.fromFile
+ */
+inline fun File.toUri(): Uri = Uri.fromFile(this)
+
+/** Creates a [File] from the given [Uri]. */
+inline fun Uri.toFile(): File = File(path)
diff --git a/core/ktx/src/main/java/androidx/core/os/Bundle.kt b/core/ktx/src/main/java/androidx/core/os/Bundle.kt
new file mode 100644
index 0000000..53da45d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Bundle.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.os
+
+import android.os.Binder
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.Size
+import android.util.SizeF
+import java.io.Serializable
+
+/**
+ * Returns a new [Bundle] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [Bundle].
+ */
+fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply {
+ for ((key, value) in pairs) {
+ when (value) {
+ null -> putString(key, null) // Any nullable type will suffice.
+
+ // Scalars
+ is Boolean -> putBoolean(key, value)
+ is Byte -> putByte(key, value)
+ is Char -> putChar(key, value)
+ is Double -> putDouble(key, value)
+ is Float -> putFloat(key, value)
+ is Int -> putInt(key, value)
+ is Long -> putLong(key, value)
+ is Short -> putShort(key, value)
+
+ // References
+ is Bundle -> putBundle(key, value)
+ is CharSequence -> putCharSequence(key, value)
+ is Parcelable -> putParcelable(key, value)
+
+ // Scalar arrays
+ is BooleanArray -> putBooleanArray(key, value)
+ is ByteArray -> putByteArray(key, value)
+ is CharArray -> putCharArray(key, value)
+ is DoubleArray -> putDoubleArray(key, value)
+ is FloatArray -> putFloatArray(key, value)
+ is IntArray -> putIntArray(key, value)
+ is LongArray -> putLongArray(key, value)
+ is ShortArray -> putShortArray(key, value)
+
+ // Reference arrays
+ is Array<*> -> {
+ val componentType = value::class.java.componentType
+ @Suppress("UNCHECKED_CAST") // Checked by reflection.
+ when {
+ Parcelable::class.java.isAssignableFrom(componentType) -> {
+ putParcelableArray(key, value as Array<Parcelable>)
+ }
+ String::class.java.isAssignableFrom(componentType) -> {
+ putStringArray(key, value as Array<String>)
+ }
+ CharSequence::class.java.isAssignableFrom(componentType) -> {
+ putCharSequenceArray(key, value as Array<CharSequence>)
+ }
+ Serializable::class.java.isAssignableFrom(componentType) -> {
+ putSerializable(key, value)
+ }
+ else -> {
+ val valueType = componentType.canonicalName
+ throw IllegalArgumentException(
+ "Illegal value array type $valueType for key \"$key\"")
+ }
+ }
+ }
+
+ // Last resort. Also we must check this after Array<*> as all arrays are serializable.
+ is Serializable -> putSerializable(key, value)
+
+ else -> {
+ if (Build.VERSION.SDK_INT >= 18 && value is Binder) {
+ putBinder(key, value)
+ } else if (Build.VERSION.SDK_INT >= 21 && value is Size) {
+ putSize(key, value)
+ } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) {
+ putSizeF(key, value)
+ } else {
+ val valueType = value.javaClass.canonicalName
+ throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+ }
+ }
+ }
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/Handler.kt b/core/ktx/src/main/java/androidx/core/os/Handler.kt
new file mode 100644
index 0000000..de09a9b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Handler.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.os
+
+import android.os.Handler
+
+/**
+ * Version of [Handler.postDelayed] which re-orders the parameters, allowing the action to be
+ * placed outside of parentheses.
+ *
+ * ```
+ * handler.postDelayed(200) {
+ * doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+inline fun Handler.postDelayed(
+ delayInMillis: Long,
+ token: Any? = null,
+ crossinline action: () -> Unit
+): Runnable {
+ val runnable = Runnable { action() }
+ if (token == null) {
+ postDelayed(runnable, delayInMillis)
+ } else {
+ HandlerCompat.postDelayed(this, runnable, token, delayInMillis)
+ }
+ return runnable
+}
+
+/**
+ * Version of [Handler.postAtTime] which re-orders the parameters, allowing the action to be
+ * placed outside of parentheses.
+ *
+ * ```
+ * handler.postAtTime(200) {
+ * doSomething()
+ * }
+ * ```
+ *
+ * @param token An optional object with which the posted message will be associated.
+ * @return the created Runnable
+ */
+inline fun Handler.postAtTime(
+ uptimeMillis: Long,
+ token: Any? = null,
+ crossinline action: () -> Unit
+): Runnable {
+ val runnable = Runnable { action() }
+ postAtTime(runnable, token, uptimeMillis)
+ return runnable
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt b/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt
new file mode 100644
index 0000000..261e72b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.os
+
+import android.os.Build
+import android.os.PersistableBundle
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns a new [PersistableBundle] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [PersistableBundle].
+ */
+@RequiresApi(21)
+fun persistableBundleOf(vararg pairs: Pair<String, Any?>) = PersistableBundle(pairs.size).apply {
+ for ((key, value) in pairs) {
+ when (value) {
+ null -> putString(key, null) // Any nullable type will suffice.
+
+ // Scalars
+ is Boolean -> {
+ if (Build.VERSION.SDK_INT >= 22) {
+ putBoolean(key, value)
+ } else {
+ throw IllegalArgumentException("Illegal value type boolean for key \"$key\"")
+ }
+ }
+ is Double -> putDouble(key, value)
+ is Int -> putInt(key, value)
+ is Long -> putLong(key, value)
+
+ // References
+ is String -> putString(key, value)
+
+ // Scalar arrays
+ is BooleanArray -> {
+ if (Build.VERSION.SDK_INT >= 22) {
+ putBooleanArray(key, value)
+ } else {
+ throw IllegalArgumentException("Illegal value type boolean[] for key \"$key\"")
+ }
+ }
+ is DoubleArray -> putDoubleArray(key, value)
+ is IntArray -> putIntArray(key, value)
+ is LongArray -> putLongArray(key, value)
+
+ // Reference arrays
+ is Array<*> -> {
+ val componentType = value::class.java.componentType
+ @Suppress("UNCHECKED_CAST") // Checked by reflection.
+ when {
+ String::class.java.isAssignableFrom(componentType) -> {
+ putStringArray(key, value as Array<String>)
+ }
+ else -> {
+ val valueType = componentType.canonicalName
+ throw IllegalArgumentException(
+ "Illegal value array type $valueType for key \"$key\"")
+ }
+ }
+ }
+
+ else -> {
+ val valueType = value.javaClass.canonicalName
+ throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+ }
+ }
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/Trace.kt b/core/ktx/src/main/java/androidx/core/os/Trace.kt
new file mode 100644
index 0000000..43bf85e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Trace.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.os
+
+import android.os.Trace
+
+/**
+ * Wrap the specified [block] in calls to [Trace.beginSection] (with the supplied [sectionName])
+ * and [Trace.endSection].
+ */
+inline fun <T> trace(sectionName: String, block: () -> T): T {
+ TraceCompat.beginSection(sectionName)
+ try {
+ return block()
+ } finally {
+ TraceCompat.endSection()
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt b/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt
new file mode 100644
index 0000000..b4f267e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.preference
+
+import android.preference.Preference
+import android.preference.PreferenceGroup
+
+/**
+ * Returns the preference with `key`.
+ *
+ * @throws NullPointerException if no preference is found with that key.
+ */
+inline operator fun PreferenceGroup.get(key: CharSequence): Preference = findPreference(key)
+
+/**
+ * Returns the preference at `index`.
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+operator fun PreferenceGroup.get(index: Int): Preference = getPreference(index)
+ ?: throw IndexOutOfBoundsException("Index: $index, Size: $preferenceCount")
+
+/** Returns `true` if `preference` is found in this preference group. */
+operator fun PreferenceGroup.contains(preference: Preference): Boolean {
+ for (index in 0 until size) {
+ if (get(index) == preference) {
+ return true
+ }
+ }
+ return false
+}
+
+/** Adds `preference` to this preference group. */
+inline operator fun PreferenceGroup.plusAssign(preference: Preference) {
+ addPreference(preference)
+}
+
+/** Removes `preference` from this preference group. */
+inline operator fun PreferenceGroup.minusAssign(preference: Preference) {
+ removePreference(preference)
+}
+
+/** Returns the number of preferences in this preference group. */
+inline val PreferenceGroup.size: Int get() = preferenceCount
+
+/** Returns true if this preference group contains no preferences. */
+inline fun PreferenceGroup.isEmpty(): Boolean = size == 0
+
+/** Returns true if this preference group contains one or more preferences. */
+inline fun PreferenceGroup.isNotEmpty(): Boolean = size != 0
+
+/** Performs the given action on each preference in this preference group. */
+inline fun PreferenceGroup.forEach(action: (preference: Preference) -> Unit) {
+ for (index in 0 until size) {
+ action(get(index))
+ }
+}
+
+/** Performs the given action on each preference in this preference group, providing its sequential index. */
+inline fun PreferenceGroup.forEachIndexed(action: (index: Int, preference: Preference) -> Unit) {
+ for (index in 0 until size) {
+ action(index, get(index))
+ }
+}
+
+/** Returns a [MutableIterator] over the preferences in this preference group. */
+operator fun PreferenceGroup.iterator() = object : MutableIterator<Preference> {
+ private var index = 0
+ override fun hasNext() = index < size
+ override fun next() = getPreference(index++) ?: throw IndexOutOfBoundsException()
+ override fun remove() {
+ removePreference(getPreference(--index))
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/text/CharSequence.kt b/core/ktx/src/main/java/androidx/core/text/CharSequence.kt
new file mode 100644
index 0000000..d41aed2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/CharSequence.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.text
+
+import android.text.TextUtils
+
+/**
+ * Returns whether the given [CharSequence] contains only digits.
+ *
+ * @see TextUtils.isDigitsOnly
+ */
+inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)
+
+/**
+ * Returns the length that the specified [CharSequence] would have if spaces and ASCII control
+ * characters were trimmed from the start and end, as by [String.trim].
+ *
+ * @see TextUtils.getTrimmedLength
+ */
+inline fun CharSequence.trimmedLength() = TextUtils.getTrimmedLength(this)
diff --git a/core/ktx/src/main/java/androidx/core/text/Html.kt b/core/ktx/src/main/java/androidx/core/text/Html.kt
new file mode 100644
index 0000000..d2636ee
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/Html.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.text
+
+import android.annotation.SuppressLint
+import android.text.Html
+import android.text.Html.FROM_HTML_MODE_LEGACY
+import android.text.Html.ImageGetter
+import android.text.Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE
+import android.text.Html.TagHandler
+import android.text.Spanned
+
+/**
+ * Returns a [Spanned] from parsing this string as HTML.
+ *
+ * @param flags Additional option to set the behavior of the HTML parsing. Default is set to
+ * [Html.FROM_HTML_MODE_LEGACY] which was introduced in API 24.
+ * @param imageGetter Returns displayable styled text from the provided HTML string.
+ * @param tagHandler Notified when HTML tags are encountered a tag the parser does
+ * not know how to interpret.
+ *
+ * @see Html.fromHtml
+ */
+fun String.parseAsHtml(
+ @SuppressLint("InlinedApi") flags: Int = FROM_HTML_MODE_LEGACY,
+ imageGetter: ImageGetter? = null,
+ tagHandler: TagHandler? = null
+): Spanned = HtmlCompat.fromHtml(this, flags, imageGetter, tagHandler)
+
+/**
+ * Returns a string of HTML from the spans in this [Spanned].
+ *
+ * @see Html.toHtml
+ */
+fun Spanned.toHtml(
+ @SuppressLint("InlinedApi") option: Int = TO_HTML_PARAGRAPH_LINES_CONSECUTIVE
+): String = HtmlCompat.toHtml(this, option)
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannableString.kt b/core/ktx/src/main/java/androidx/core/text/SpannableString.kt
new file mode 100644
index 0000000..29aa9c2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannableString.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.text
+
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+
+/**
+ * Returns a new [Spannable] from [CharSequence],
+ * or the source itself if it is already an instance of [SpannableString].
+ */
+inline fun CharSequence.toSpannable(): Spannable = SpannableString.valueOf(this)
+
+/** Adds [span] to the entire text. */
+inline operator fun Spannable.plusAssign(span: Any) =
+ setSpan(span, 0, length, SPAN_INCLUSIVE_EXCLUSIVE)
+
+/** Removes [span] from this text. */
+inline operator fun Spannable.minusAssign(span: Any) = removeSpan(span)
+
+/** Clear all spans from this text. */
+inline fun Spannable.clearSpans() = getSpans<Any>().forEach { removeSpan(it) }
+
+/**
+ * Add [span] to the range [start]…[end] of the text.
+ *
+ * ```
+ * val s = "Hello, World!".toSpannable()
+ * s[0, 5] = UnderlineSpan()
+ * ```
+ *
+ * Note: The [end] value is exclusive.
+ *
+ * @see Spannable.setSpan
+ */
+inline operator fun Spannable.set(start: Int, end: Int, span: Any) {
+ setSpan(span, start, end, SPAN_INCLUSIVE_EXCLUSIVE)
+}
+
+/**
+ * Add [span] to the [range] of the text.
+ *
+ * ```
+ * val s = "Hello, World!".toSpannable()
+ * s[0..5] = UnderlineSpan()
+ * ```
+ *
+ * Note: The range end value is exclusive.
+ *
+ * @see Spannable.setSpan
+ */
+inline operator fun Spannable.set(range: IntRange, span: Any) {
+ // This looks weird, but endInclusive is just the exact upper value.
+ setSpan(span, range.start, range.endInclusive, SPAN_INCLUSIVE_EXCLUSIVE)
+}
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt b/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt
new file mode 100644
index 0000000..5834a34
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.text
+
+import android.graphics.Typeface.BOLD
+import android.graphics.Typeface.ITALIC
+import android.text.Spannable.SPAN_INCLUSIVE_EXCLUSIVE
+import android.text.SpannableStringBuilder
+import android.text.SpannedString
+import android.text.style.BackgroundColorSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.RelativeSizeSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import androidx.annotation.ColorInt
+
+/**
+ * Builds new string by populating a newly created [SpannableStringBuilder] using the provided
+ * [builderAction] and then converting it to [SpannedString].
+ */
+inline fun buildSpannedString(builderAction: SpannableStringBuilder.() -> Unit): SpannedString {
+ val builder = SpannableStringBuilder()
+ builder.builderAction()
+ return SpannedString(builder)
+}
+
+/**
+ * Wrap appended text in [builderAction] in [spans].
+ *
+ * Note: the spans will only have the correct position if the [builderAction] only appends or
+ * replaces text. Inserting, deleting, or clearing the text will cause the span to be placed at
+ * an incorrect position.
+ */
+inline fun SpannableStringBuilder.inSpans(
+ vararg spans: Any,
+ builderAction: SpannableStringBuilder.() -> Unit
+): SpannableStringBuilder {
+ val start = length
+ builderAction()
+ for (span in spans) setSpan(span, start, length, SPAN_INCLUSIVE_EXCLUSIVE)
+ return this
+}
+
+/**
+ * Wrap appended text in [builderAction] in [span].
+ *
+ * Note: the span will only have the correct position if the `builderAction` only appends or
+ * replaces text. Inserting, deleting, or clearing the text will cause the span to be placed at
+ * an incorrect position.
+ */
+inline fun SpannableStringBuilder.inSpans(
+ span: Any,
+ builderAction: SpannableStringBuilder.() -> Unit
+): SpannableStringBuilder {
+ val start = length
+ builderAction()
+ setSpan(span, start, length, SPAN_INCLUSIVE_EXCLUSIVE)
+ return this
+}
+
+/**
+ * Wrap appended text in [builderAction] in a bold [StyleSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.bold(builderAction: SpannableStringBuilder.() -> Unit) =
+ inSpans(StyleSpan(BOLD), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in an italic [StyleSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.italic(builderAction: SpannableStringBuilder.() -> Unit) =
+ inSpans(StyleSpan(ITALIC), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in an [UnderlineSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.underline(builderAction: SpannableStringBuilder.() -> Unit) =
+ inSpans(UnderlineSpan(), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [ForegroundColorSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.color(
+ @ColorInt color: Int,
+ builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(ForegroundColorSpan(color), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [BackgroundColorSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.backgroundColor(
+ @ColorInt color: Int,
+ builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(BackgroundColorSpan(color), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [StrikethroughSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.strikeThrough(builderAction: SpannableStringBuilder.() -> Unit) =
+ inSpans(StrikethroughSpan(), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [RelativeSizeSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.scale(
+ proportion: Float,
+ builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(RelativeSizeSpan(proportion), builderAction = builderAction)
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannedString.kt b/core/ktx/src/main/java/androidx/core/text/SpannedString.kt
new file mode 100644
index 0000000..db27511
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannedString.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.text
+
+import android.text.Spanned
+import android.text.SpannedString
+
+/**
+ * Returns a new [Spanned] from [CharSequence],
+ * or the source itself if it is already an instance of [SpannedString].
+ */
+inline fun CharSequence.toSpanned(): Spanned = SpannedString.valueOf(this)
+
+/** Get all spans that are instance of [T]. */
+inline fun <reified T : Any> Spanned.getSpans(start: Int = 0, end: Int = length): Array<out T> =
+ getSpans(start, end, T::class.java)
diff --git a/core/ktx/src/main/java/androidx/core/text/String.kt b/core/ktx/src/main/java/androidx/core/text/String.kt
new file mode 100644
index 0000000..8bffdd0
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/String.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.text
+
+import android.text.TextUtils
+
+/**
+ * Html-encode the string.
+ *
+ * @see TextUtils.htmlEncode
+ */
+inline fun String.htmlEncode(): String = TextUtils.htmlEncode(this)
diff --git a/core/ktx/src/main/java/androidx/core/transition/Transition.kt b/core/ktx/src/main/java/androidx/core/transition/Transition.kt
new file mode 100644
index 0000000..b79f2cb
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/transition/Transition.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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 androidx.core.transition
+
+import android.transition.Transition
+import androidx.annotation.RequiresApi
+
+/**
+ * Add an action which will be invoked when this transition has ended.
+ */
+@RequiresApi(19)
+fun Transition.doOnEnd(action: (transition: Transition) -> Unit) {
+ addListener(onEnd = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has started.
+ */
+@RequiresApi(19)
+fun Transition.doOnStart(action: (transition: Transition) -> Unit) {
+ addListener(onStart = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has been cancelled.
+ */
+@RequiresApi(19)
+fun Transition.doOnCancel(action: (transition: Transition) -> Unit) {
+ addListener(onCancel = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has resumed after a pause.
+ */
+@RequiresApi(19)
+fun Transition.doOnResume(action: (transition: Transition) -> Unit) {
+ addListener(onResume = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has been paused.
+ */
+@RequiresApi(19)
+fun Transition.doOnPause(action: (transition: Transition) -> Unit) {
+ addListener(onPause = action)
+}
+
+/**
+ * Add a listener to this Transition using the provided actions.
+ */
+@RequiresApi(19)
+fun Transition.addListener(
+ onEnd: ((transition: Transition) -> Unit)? = null,
+ onStart: ((transition: Transition) -> Unit)? = null,
+ onCancel: ((transition: Transition) -> Unit)? = null,
+ onResume: ((transition: Transition) -> Unit)? = null,
+ onPause: ((transition: Transition) -> Unit)? = null
+) {
+ addListener(object : Transition.TransitionListener {
+ override fun onTransitionEnd(transition: Transition) {
+ onEnd?.invoke(transition)
+ }
+
+ override fun onTransitionResume(transition: Transition) {
+ onResume?.invoke(transition)
+ }
+
+ override fun onTransitionPause(transition: Transition) {
+ onPause?.invoke(transition)
+ }
+
+ override fun onTransitionCancel(transition: Transition) {
+ onCancel?.invoke(transition)
+ }
+
+ override fun onTransitionStart(transition: Transition) {
+ onStart?.invoke(transition)
+ }
+ })
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt b/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt
new file mode 100644
index 0000000..b676403
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.ArrayMap
+import androidx.annotation.RequiresApi
+import kotlin.Pair
+
+/** Returns an empty new [ArrayMap]. */
+@RequiresApi(19)
+inline fun <K, V> arrayMapOf(): ArrayMap<K, V> = ArrayMap()
+
+/**
+ * Returns a new [ArrayMap] with the specified contents, given as a list of pairs where the first
+ * component is the key and the second component is the value.
+ *
+ * If multiple pairs have the same key, the resulting map will contain the value from the last of
+ * those pairs.
+ */
+@RequiresApi(19)
+fun <K, V> arrayMapOf(vararg pairs: Pair<K, V>): ArrayMap<K, V> {
+ val map = ArrayMap<K, V>(pairs.size)
+ for (pair in pairs) {
+ map[pair.first] = pair.second
+ }
+ return map
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/ArraySet.kt b/core/ktx/src/main/java/androidx/core/util/ArraySet.kt
new file mode 100644
index 0000000..6773d23
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/ArraySet.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.ArraySet
+import androidx.annotation.RequiresApi
+
+/** Returns an empty new [ArraySet]. */
+@RequiresApi(23)
+inline fun <T> arraySetOf(): ArraySet<T> = ArraySet()
+
+/** Returns a new [ArraySet] with the specified contents. */
+@RequiresApi(23)
+fun <T> arraySetOf(vararg values: T): ArraySet<T> {
+ val set = ArraySet<T>(values.size)
+ @Suppress("LoopToCallChain") // Causes needless copy to a list.
+ for (value in values) {
+ set.add(value)
+ }
+ return set
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt b/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt
new file mode 100644
index 0000000..b8fbba7
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.util
+
+import android.util.AtomicFile
+import androidx.annotation.RequiresApi
+import java.io.FileOutputStream
+import java.nio.charset.Charset
+
+/**
+ * Perform the write operations inside [block] on this file. If [block] throws an exception the
+ * write will be failed. Otherwise the write will be applied atomically to the file.
+ */
+@RequiresApi(17)
+inline fun AtomicFile.tryWrite(block: (out: FileOutputStream) -> Unit) {
+ val stream = startWrite()
+ var success = false
+ try {
+ block(stream)
+ success = true
+ } finally {
+ if (success) {
+ finishWrite(stream)
+ } else {
+ failWrite(stream)
+ }
+ }
+}
+
+/**
+ * Sets the content of this file as an [array] of bytes.
+ */
+@RequiresApi(17)
+fun AtomicFile.writeBytes(array: ByteArray) {
+ tryWrite {
+ it.write(array)
+ }
+}
+
+/**
+ * Sets the content of this file as [text] encoded using UTF-8 or specified [charset].
+ * If this file exists, it becomes overwritten.
+ */
+@RequiresApi(17)
+fun AtomicFile.writeText(text: String, charset: Charset = Charsets.UTF_8) {
+ writeBytes(text.toByteArray(charset))
+}
+
+/**
+ * Gets the entire content of this file as a byte array.
+ *
+ * This method is not recommended on huge files. It has an internal limitation of 2 GB file size.
+ */
+@RequiresApi(17)
+inline fun AtomicFile.readBytes(): ByteArray = readFully()
+
+/**
+ * Gets the entire content of this file as a String using UTF-8 or specified [charset].
+ *
+ * This method is not recommended on huge files. It has an internal limitation of 2 GB file size.
+ */
+@RequiresApi(17)
+fun AtomicFile.readText(charset: Charset = Charsets.UTF_8): String {
+ return readFully().toString(charset)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/Half.kt b/core/ktx/src/main/java/androidx/core/util/Half.kt
new file mode 100644
index 0000000..409c01e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Half.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.util
+
+import android.util.Half
+import androidx.annotation.HalfFloat
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns a [Half] instance representing given [Short].
+ *
+ * @see Half.valueOf
+ */
+// TODO https://youtrack.jetbrains.com/issue/KT-21696
+@Suppress("WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+@RequiresApi(26)
+inline fun @receiver:HalfFloat Short.toHalf(): Half = Half.valueOf(this)
+
+/**
+ * Returns a [Half] instance representing given [Float].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun Float.toHalf(): Half = Half.valueOf(this)
+
+/**
+ * Returns a [Half] instance representing given [Double].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun Double.toHalf(): Half = toFloat().toHalf()
+
+/**
+ * Returns a [Half] instance representing given [String].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun String.toHalf(): Half = Half.valueOf(this)
diff --git a/core/ktx/src/main/java/androidx/core/util/Locale.kt b/core/ktx/src/main/java/androidx/core/util/Locale.kt
new file mode 100644
index 0000000..2b4312e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Locale.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import android.text.TextUtils
+import androidx.annotation.RequiresApi
+import java.util.Locale
+
+/**
+ * Returns layout direction for a given locale.
+ * @see TextUtils.getLayoutDirectionFromLocale
+ */
+val Locale.layoutDirection: Int
+ @RequiresApi(17)
+ get() = TextUtils.getLayoutDirectionFromLocale(this)
diff --git a/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt b/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt
new file mode 100644
index 0000000..cd9eb4c
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.LongSparseArray
+import androidx.annotation.RequiresApi
+
+/** Returns the number of key/value pairs in the collection. */
+@get:RequiresApi(16)
+inline val <T> LongSparseArray<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(16)
+inline operator fun <T> LongSparseArray<T>.contains(key: Long) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+@RequiresApi(16)
+inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+@RequiresApi(16)
+operator fun <T> LongSparseArray<T>.plus(other: LongSparseArray<T>): LongSparseArray<T> {
+ val new = LongSparseArray<T>(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.containsKey(key: Long) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.getOrDefault(key: Long, defaultValue: T) =
+ get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.getOrElse(key: Long, defaultValue: () -> T) =
+ get(key) ?: defaultValue()
+
+/** Return true when the collection contains no elements. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.remove(key: Long, value: T): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.putAll(other: LongSparseArray<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.forEach(action: (key: Long, value: T) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.keyIterator(): LongIterator = object : LongIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextLong() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun next() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/LruCache.kt b/core/ktx/src/main/java/androidx/core/util/LruCache.kt
new file mode 100644
index 0000000..44c2f8d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/LruCache.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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 androidx.core.util
+
+import android.util.LruCache
+
+/**
+ * Creates an [LruCache] with the given parameters.
+ *
+ * @param maxSize for caches that do not specify [sizeOf], this is
+ * the maximum number of entries in the cache. For all other caches,
+ * this is the maximum sum of the sizes of the entries in this cache.
+ * @param sizeOf function that returns the size of the entry for key and value in
+ * user-defined units. The default implementation returns 1.
+ * @param create a create called after a cache miss to compute a value for the corresponding key.
+ * Returns the computed value or null if no value can be computed. The default implementation
+ * returns null.
+ * @param onEntryRemoved a function called for entries that have been evicted or removed.
+ *
+ * @see LruCache.sizeOf
+ * @see LruCache.create
+ * @see LruCache.entryRemoved
+ */
+inline fun <K : Any, V : Any> lruCache(
+ maxSize: Int,
+ crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
+ @Suppress("USELESS_CAST") // https://youtrack.jetbrains.com/issue/KT-21946
+ crossinline create: (key: K) -> V? = { null as V? },
+ crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
+ { _, _, _, _ -> }
+): LruCache<K, V> {
+ return object : LruCache<K, V>(maxSize) {
+ override fun sizeOf(key: K, value: V) = sizeOf(key, value)
+ override fun create(key: K) = create(key)
+ override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
+ onEntryRemoved(evicted, key, oldValue, newValue)
+ }
+ }
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/Pair.kt b/core/ktx/src/main/java/androidx/core/util/Pair.kt
new file mode 100644
index 0000000..cf12156
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Pair.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.Pair
+
+/**
+ * Returns the first component of the pair.
+ *
+ * This method allows to use destructuring declarations when working with pairs, for example:
+ * ```
+ * val (first, second) = myPair
+ * ```
+ */
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline operator fun <F, S> Pair<F, S>.component1() = first
+
+/**
+ * Returns the second component of the pair.
+ *
+ * This method allows to use destructuring declarations when working with pairs, for example:
+ * ```
+ * val (first, second) = myPair
+ * ```
+ */
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline operator fun <F, S> Pair<F, S>.component2() = second
+
+/** Returns this [Pair] as a [kotlin.Pair]. */
+inline fun <F, S> Pair<F, S>.toKotlinPair() = kotlin.Pair(first, second)
+
+/** Returns this [kotlin.Pair] as an Android [Pair]. */
+// Note: the return type is explicitly specified here to prevent always seeing platform types.
+inline fun <F, S> kotlin.Pair<F, S>.toAndroidPair(): Pair<F, S> = Pair(first, second)
diff --git a/core/ktx/src/main/java/androidx/core/util/Range.kt b/core/ktx/src/main/java/androidx/core/util/Range.kt
new file mode 100644
index 0000000..b5f7eb6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Range.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.Range
+import androidx.annotation.RequiresApi
+
+/**
+ * Creates a range from this [Comparable] value to [that].
+ *
+ * @throws IllegalArgumentException if this value is comparatively smaller than [that].
+ */
+@RequiresApi(21)
+inline infix fun <T : Comparable<T>> T.rangeTo(that: T): Range<T> = Range(this, that)
+
+/** Return the smallest range that includes this and [value]. */
+@RequiresApi(21)
+inline operator fun <T : Comparable<T>> Range<T>.plus(value: T): Range<T> = extend(value)
+
+/** Return the smallest range that includes this and [other]. */
+@RequiresApi(21)
+inline operator fun <T : Comparable<T>> Range<T>.plus(other: Range<T>): Range<T> = extend(other)
+
+/**
+ * Return the intersection of this range and [other].
+ *
+ * @throws IllegalArgumentException if this is disjoint from [other].
+ */
+@RequiresApi(21)
+inline infix fun <T : Comparable<T>> Range<T>.and(other: Range<T>): Range<T> = intersect(other)
+
+/** Returns this [Range] as a [ClosedRange]. */
+@RequiresApi(21)
+fun <T : Comparable<T>> Range<T>.toClosedRange(): ClosedRange<T> = object : ClosedRange<T> {
+ override val endInclusive get() = upper
+ override val start get() = lower
+}
+
+/** Returns this [ClosedRange] as a [Range]. */
+@RequiresApi(21)
+fun <T : Comparable<T>> ClosedRange<T>.toRange(): Range<T> = Range(start, endInclusive)
diff --git a/core/ktx/src/main/java/androidx/core/util/Size.kt b/core/ktx/src/main/java/androidx/core/util/Size.kt
new file mode 100644
index 0000000..1cb7858
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Size.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 20188 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.util
+
+import android.util.Size
+import android.util.SizeF
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns "width", the first component of this [Size].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun Size.component1() = width
+
+/**
+ * Returns "height", the second component of this [Size].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun Size.component2() = height
+
+/**
+ * Returns "width", the first component of this [SizeF].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun SizeF.component1() = width
+
+/**
+ * Returns "height", the second component of this [SizeF].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun SizeF.component2() = height
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseArray.kt
new file mode 100644
index 0000000..2b34a46
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseArray.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val <T> SparseArray<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun <T> SparseArray<T>.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun <T> SparseArray<T>.set(key: Int, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun <T> SparseArray<T>.plus(other: SparseArray<T>): SparseArray<T> {
+ val new = SparseArray<T>(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun <T> SparseArray<T>.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun <T> SparseArray<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun <T> SparseArray<T>.getOrDefault(key: Int, defaultValue: T) = get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun <T> SparseArray<T>.getOrElse(key: Int, defaultValue: () -> T) =
+ get(key) ?: defaultValue()
+
+/** Return true when the collection contains no elements. */
+inline fun <T> SparseArray<T>.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun <T> SparseArray<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun <T> SparseArray<T>.remove(key: Int, value: T): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun <T> SparseArray<T>.putAll(other: SparseArray<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun <T> SparseArray<T>.forEach(action: (key: Int, value: T) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+fun <T> SparseArray<T>.keyIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun <T> SparseArray<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun next() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt
new file mode 100644
index 0000000..b349326
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseBooleanArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val SparseBooleanArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun SparseBooleanArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun SparseBooleanArray.set(key: Int, value: Boolean) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun SparseBooleanArray.plus(other: SparseBooleanArray): SparseBooleanArray {
+ val new = SparseBooleanArray(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun SparseBooleanArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun SparseBooleanArray.containsValue(value: Boolean) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun SparseBooleanArray.getOrDefault(key: Int, defaultValue: Boolean) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun SparseBooleanArray.getOrElse(key: Int, defaultValue: () -> Boolean) =
+ indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+inline fun SparseBooleanArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun SparseBooleanArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun SparseBooleanArray.remove(key: Int, value: Boolean): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ // Delete by key because of https://issuetracker.google.com/issues/70934959.
+ delete(key)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun SparseBooleanArray.putAll(other: SparseBooleanArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun SparseBooleanArray.forEach(action: (key: Int, value: Boolean) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+fun SparseBooleanArray.keyIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun SparseBooleanArray.valueIterator(): BooleanIterator = object : BooleanIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextBoolean() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt
new file mode 100644
index 0000000..bba57c6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseIntArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val SparseIntArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun SparseIntArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun SparseIntArray.set(key: Int, value: Int) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun SparseIntArray.plus(other: SparseIntArray): SparseIntArray {
+ val new = SparseIntArray(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun SparseIntArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun SparseIntArray.containsValue(value: Int) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun SparseIntArray.getOrDefault(key: Int, defaultValue: Int) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun SparseIntArray.getOrElse(key: Int, defaultValue: () -> Int) =
+ indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+inline fun SparseIntArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun SparseIntArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun SparseIntArray.remove(key: Int, value: Int): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun SparseIntArray.putAll(other: SparseIntArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun SparseIntArray.forEach(action: (key: Int, value: Int) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+fun SparseIntArray.keyIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun SparseIntArray.valueIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt
new file mode 100644
index 0000000..68b4763
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseLongArray
+import androidx.annotation.RequiresApi
+
+/** Returns the number of key/value entries in the collection. */
+@get:RequiresApi(18)
+inline val SparseLongArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(18)
+inline operator fun SparseLongArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+@RequiresApi(18)
+inline operator fun SparseLongArray.set(key: Int, value: Long) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+@RequiresApi(18)
+operator fun SparseLongArray.plus(other: SparseLongArray): SparseLongArray {
+ val new = SparseLongArray(size() + other.size())
+ new.putAll(this)
+ new.putAll(other)
+ return new
+}
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(18)
+inline fun SparseLongArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+@RequiresApi(18)
+inline fun SparseLongArray.containsValue(value: Long) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+@RequiresApi(18)
+inline fun SparseLongArray.getOrDefault(key: Int, defaultValue: Long) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+@RequiresApi(18)
+inline fun SparseLongArray.getOrElse(key: Int, defaultValue: () -> Long) =
+ indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+@RequiresApi(18)
+inline fun SparseLongArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+@RequiresApi(18)
+inline fun SparseLongArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is set to [value]. */
+@RequiresApi(18)
+fun SparseLongArray.remove(key: Int, value: Long): Boolean {
+ val index = indexOfKey(key)
+ if (index != -1 && value == valueAt(index)) {
+ removeAt(index)
+ return true
+ }
+ return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+@RequiresApi(18)
+fun SparseLongArray.putAll(other: SparseLongArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+@RequiresApi(18)
+inline fun SparseLongArray.forEach(action: (key: Int, value: Long) -> Unit) {
+ for (index in 0 until size()) {
+ action(keyAt(index), valueAt(index))
+ }
+}
+
+/** Return an iterator over the collection's keys. */
+@RequiresApi(18)
+fun SparseLongArray.keyIterator(): IntIterator = object : IntIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+@RequiresApi(18)
+fun SparseLongArray.valueIterator(): LongIterator = object : LongIterator() {
+ var index = 0
+ override fun hasNext() = index < size()
+ override fun nextLong() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/Menu.kt b/core/ktx/src/main/java/androidx/core/view/Menu.kt
new file mode 100644
index 0000000..7ab479f
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/Menu.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.view
+
+import android.view.Menu
+import android.view.MenuItem
+
+/**
+ * Returns the menu at [index].
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+inline operator fun Menu.get(index: Int): MenuItem = getItem(index)
+
+/** Returns `true` if [item] is found in this menu. */
+operator fun Menu.contains(item: MenuItem): Boolean {
+ @Suppress("LoopToCallChain")
+ for (index in 0 until size) {
+ if (getItem(index) == item) {
+ return true
+ }
+ }
+ return false
+}
+
+/** Returns the number of items in this menu. */
+inline val Menu.size get() = size()
+
+/** Returns true if this menu contains no items. */
+inline fun Menu.isEmpty() = size() == 0
+
+/** Returns true if this menu contains one or more items. */
+inline fun Menu.isNotEmpty() = size() != 0
+
+/** Performs the given action on each item in this menu. */
+inline fun Menu.forEach(action: (item: MenuItem) -> Unit) {
+ for (index in 0 until size()) {
+ action(getItem(index))
+ }
+}
+
+/** Performs the given action on each item in this menu, providing its sequential index. */
+inline fun Menu.forEachIndexed(action: (index: Int, item: MenuItem) -> Unit) {
+ for (index in 0 until size()) {
+ action(index, getItem(index))
+ }
+}
+
+/** Returns a [MutableIterator] over the items in this menu. */
+operator fun Menu.iterator() = object : MutableIterator<MenuItem> {
+ private var index = 0
+ override fun hasNext() = index < size()
+ override fun next() = getItem(index++) ?: throw IndexOutOfBoundsException()
+ override fun remove() = removeItem(--index)
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/View.kt b/core/ktx/src/main/java/androidx/core/view/View.kt
new file mode 100644
index 0000000..fcb0256
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/View.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.view
+
+import android.graphics.Bitmap
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.accessibility.AccessibilityEvent
+import androidx.annotation.Px
+import androidx.annotation.RequiresApi
+import androidx.annotation.StringRes
+import androidx.core.graphics.applyCanvas
+
+/**
+ * Performs the given action when this view is next laid out.
+ *
+ * @see doOnLayout
+ */
+inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) {
+ addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ view.removeOnLayoutChangeListener(this)
+ action(view)
+ }
+ })
+}
+
+/**
+ * Performs the given action when this view is laid out. If the view has been laid out and it
+ * has not requested a layout, the action will be performed straight away, otherwise the
+ * action will be performed after the view is next laid out.
+ *
+ * @see doOnNextLayout
+ */
+inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
+ if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
+ action(this)
+ } else {
+ doOnNextLayout {
+ action(it)
+ }
+ }
+}
+
+/**
+ * Performs the given action when the view tree is about to be drawn.
+ */
+inline fun View.doOnPreDraw(crossinline action: (view: View) -> Unit) {
+ val vto = viewTreeObserver
+ vto.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ action(this@doOnPreDraw)
+ when {
+ vto.isAlive -> vto.removeOnPreDrawListener(this)
+ else -> viewTreeObserver.removeOnPreDrawListener(this)
+ }
+ return true
+ }
+ })
+}
+
+/**
+ * Sends [AccessibilityEvent] of type [AccessibilityEvent.TYPE_ANNOUNCEMENT].
+ *
+ * @see View.announceForAccessibility
+ */
+@RequiresApi(16)
+inline fun View.announceForAccessibility(@StringRes resource: Int) {
+ val announcement = resources.getString(resource)
+ announceForAccessibility(announcement)
+}
+
+/**
+ * Updates this view's relative padding. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see View.setPaddingRelative
+ */
+@RequiresApi(17)
+inline fun View.updatePaddingRelative(
+ @Px start: Int = paddingStart,
+ @Px top: Int = paddingTop,
+ @Px end: Int = paddingEnd,
+ @Px bottom: Int = paddingBottom
+) {
+ setPaddingRelative(start, top, end, bottom)
+}
+
+/**
+ * Updates this view's padding. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see View.setPadding
+ */
+inline fun View.updatePadding(
+ @Px left: Int = paddingLeft,
+ @Px top: Int = paddingTop,
+ @Px right: Int = paddingRight,
+ @Px bottom: Int = paddingBottom
+) {
+ setPadding(left, top, right, bottom)
+}
+
+/**
+ * Sets the view's padding. This version of the method sets all axes to the provided size.
+ *
+ * @see View.setPadding
+ */
+inline fun View.setPadding(@Px size: Int) {
+ setPadding(size, size, size, size)
+}
+
+/**
+ * Version of [View.postDelayed] which re-orders the parameters, allowing the action to be placed
+ * outside of parentheses.
+ *
+ * ```
+ * view.postDelayed(200) {
+ * doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+inline fun View.postDelayed(delayInMillis: Long, crossinline action: () -> Unit): Runnable {
+ val runnable = Runnable { action() }
+ postDelayed(runnable, delayInMillis)
+ return runnable
+}
+
+/**
+ * Version of [View.postOnAnimationDelayed] which re-orders the parameters, allowing the action
+ * to be placed outside of parentheses.
+ *
+ * ```
+ * view.postOnAnimationDelayed(16) {
+ * doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+@RequiresApi(16)
+inline fun View.postOnAnimationDelayed(
+ delayInMillis: Long,
+ crossinline action: () -> Unit
+): Runnable {
+ val runnable = Runnable { action() }
+ postOnAnimationDelayed(runnable, delayInMillis)
+ return runnable
+}
+
+/**
+ * Return a [Bitmap] representation of this [View].
+ *
+ * The resulting bitmap will be the same width and height as this view's current layout
+ * dimensions. This does not take into account any transformations such as scale or translation.
+ *
+ * Note, this will use the software rendering pipeline to draw the view to the bitmap. This may
+ * result with different drawing to what is rendered on a hardware accelerated canvas (such as
+ * the device screen).
+ *
+ * If this view has not been laid out this method will throw a [IllegalStateException].
+ *
+ * @param config Bitmap config of the desired bitmap. Defaults to [Bitmap.Config.ARGB_8888].
+ */
+fun View.toBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
+ if (!ViewCompat.isLaidOut(this)) {
+ throw IllegalStateException("View needs to be laid out before calling toBitmap()")
+ }
+ return Bitmap.createBitmap(width, height, config).applyCanvas(::draw)
+}
+
+/**
+ * Returns true when this view's visibility is [View.VISIBLE], false otherwise.
+ *
+ * ```
+ * if (view.isVisible) {
+ * // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.VISIBLE], false to [View.GONE].
+ *
+ * ```
+ * view.isVisible = true
+ * ```
+ */
+inline var View.isVisible: Boolean
+ get() = visibility == View.VISIBLE
+ set(value) {
+ visibility = if (value) View.VISIBLE else View.GONE
+ }
+
+/**
+ * Returns true when this view's visibility is [View.INVISIBLE], false otherwise.
+ *
+ * ```
+ * if (view.isInvisible) {
+ * // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.INVISIBLE], false to [View.VISIBLE].
+ *
+ * ```
+ * view.isInvisible = true
+ * ```
+ */
+inline var View.isInvisible: Boolean
+ get() = visibility == View.INVISIBLE
+ set(value) {
+ visibility = if (value) View.INVISIBLE else View.VISIBLE
+ }
+
+/**
+ * Returns true when this view's visibility is [View.GONE], false otherwise.
+ *
+ * ```
+ * if (view.isGone) {
+ * // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.GONE], false to [View.VISIBLE].
+ *
+ * ```
+ * view.isGone = true
+ * ```
+ */
+inline var View.isGone: Boolean
+ get() = visibility == View.GONE
+ set(value) {
+ visibility = if (value) View.GONE else View.VISIBLE
+ }
+
+/**
+ * Executes [block] with the View's layoutParams and reassigns the layoutParams with the
+ * updated version.
+ *
+ * @see View.getLayoutParams
+ * @see View.setLayoutParams
+ **/
+inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) {
+ updateLayoutParams<ViewGroup.LayoutParams>(block)
+}
+
+/**
+ * Executes [block] with a typed version of the View's layoutParams and reassigns the
+ * layoutParams with the updated version.
+ *
+ * @see View.getLayoutParams
+ * @see View.setLayoutParams
+ **/
+@JvmName("updateLayoutParamsTyped")
+inline fun <reified T : ViewGroup.LayoutParams> View.updateLayoutParams(block: T.() -> Unit) {
+ val params = layoutParams as T
+ block(params)
+ layoutParams = params
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt b/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt
new file mode 100644
index 0000000..8a7152a
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.view
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.Px
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns the view at [index].
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+operator fun ViewGroup.get(index: Int) =
+ getChildAt(index) ?: throw IndexOutOfBoundsException("Index: $index, Size: $childCount")
+
+/** Returns `true` if [view] is found in this view group. */
+inline operator fun ViewGroup.contains(view: View) = indexOfChild(view) != -1
+
+/** Adds [view] to this view group. */
+inline operator fun ViewGroup.plusAssign(view: View) = addView(view)
+
+/** Removes [view] from this view group. */
+inline operator fun ViewGroup.minusAssign(view: View) = removeView(view)
+
+/** Returns the number of views in this view group. */
+inline val ViewGroup.size get() = childCount
+
+/** Returns true if this view group contains no views. */
+inline fun ViewGroup.isEmpty() = childCount == 0
+
+/** Returns true if this view group contains one or more views. */
+inline fun ViewGroup.isNotEmpty() = childCount != 0
+
+/** Performs the given action on each view in this view group. */
+inline fun ViewGroup.forEach(action: (view: View) -> Unit) {
+ for (index in 0 until childCount) {
+ action(getChildAt(index))
+ }
+}
+
+/** Performs the given action on each view in this view group, providing its sequential index. */
+inline fun ViewGroup.forEachIndexed(action: (index: Int, view: View) -> Unit) {
+ for (index in 0 until childCount) {
+ action(index, getChildAt(index))
+ }
+}
+
+/** Returns a [MutableIterator] over the views in this view group. */
+operator fun ViewGroup.iterator() = object : MutableIterator<View> {
+ private var index = 0
+ override fun hasNext() = index < childCount
+ override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException()
+ override fun remove() = removeViewAt(--index)
+}
+
+/** Returns a [Sequence] over the child views in this view group. */
+val ViewGroup.children: Sequence<View>
+ get() = object : Sequence<View> {
+ override fun iterator() = this@children.iterator()
+ }
+
+/**
+ * Sets the margins in the ViewGroup's MarginLayoutParams. This version of the method sets all axes
+ * to the provided size.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+inline fun ViewGroup.MarginLayoutParams.setMargins(@Px size: Int) {
+ setMargins(size, size, size, size)
+}
+
+/**
+ * Updates the margins in the [ViewGroup]'s [ViewGroup.MarginLayoutParams].
+ * This version of the method allows using named parameters to just set one or more axes.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+inline fun ViewGroup.MarginLayoutParams.updateMargins(
+ @Px left: Int = leftMargin,
+ @Px top: Int = topMargin,
+ @Px right: Int = rightMargin,
+ @Px bottom: Int = bottomMargin
+) {
+ setMargins(left, top, right, bottom)
+}
+
+/**
+ * Updates the relative margins in the ViewGroup's MarginLayoutParams.
+ * This version of the method allows using named parameters to just set one or more axes.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+@RequiresApi(17)
+inline fun ViewGroup.MarginLayoutParams.updateMarginsRelative(
+ @Px start: Int = marginStart,
+ @Px top: Int = topMargin,
+ @Px end: Int = marginEnd,
+ @Px bottom: Int = bottomMargin
+) {
+ marginStart = start
+ topMargin = top
+ marginEnd = end
+ bottomMargin = bottom
+}
diff --git a/core/ktx/src/main/java/androidx/core/widget/Toast.kt b/core/ktx/src/main/java/androidx/core/widget/Toast.kt
new file mode 100644
index 0000000..b0ddfd1
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/widget/Toast.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.widget
+
+import android.content.Context
+import android.widget.Toast
+import androidx.annotation.StringRes
+
+/**
+ * Creates and shows a [Toast] with the given [text]
+ *
+ * @param duration Toast duration, defaults to [Toast.LENGTH_SHORT]
+ */
+inline fun Context.toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT): Toast {
+ return Toast.makeText(this, text, duration).apply { show() }
+}
+
+/**
+ * Creates and shows a [Toast] with text from a resource
+ *
+ * @param resId Resource id of the string resource to use
+ * @param duration Toast duration, defaults to [Toast.LENGTH_SHORT]
+ */
+inline fun Context.toast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT): Toast {
+ return Toast.makeText(this, resId, duration).apply { show() }
+}
diff --git a/emoji/core/api/current.txt b/emoji/core/api/current.txt
index 785f44a..7b343a1 100644
--- a/emoji/core/api/current.txt
+++ b/emoji/core/api/current.txt
@@ -9,6 +9,7 @@
method public boolean hasEmojiGlyph(java.lang.CharSequence);
method public boolean hasEmojiGlyph(java.lang.CharSequence, int);
method public static androidx.emoji.text.EmojiCompat init(androidx.emoji.text.EmojiCompat.Config);
+ method public void load();
method public java.lang.CharSequence process(java.lang.CharSequence);
method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
@@ -17,9 +18,12 @@
method public void unregisterInitCallback(androidx.emoji.text.EmojiCompat.InitCallback);
field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+ field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
field public static final int LOAD_STATE_FAILED = 2; // 0x2
field public static final int LOAD_STATE_LOADING = 0; // 0x0
field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+ field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
@@ -31,6 +35,7 @@
method public androidx.emoji.text.EmojiCompat.Config registerInitCallback(androidx.emoji.text.EmojiCompat.InitCallback);
method public androidx.emoji.text.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
method public androidx.emoji.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+ method public androidx.emoji.text.EmojiCompat.Config setMetadataLoadStrategy(int);
method public androidx.emoji.text.EmojiCompat.Config setReplaceAll(boolean);
method public androidx.emoji.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
method public androidx.emoji.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer>);
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/text/ConfigTest.java b/emoji/core/src/androidTest/java/androidx/emoji/text/ConfigTest.java
index 34f31fd..c8d3dad 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/text/ConfigTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/text/ConfigTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -148,9 +149,25 @@
assertTrue(emojiCompat.isEmojiSpanIndicatorEnabled());
}
+ @Test
+ public void testBuild_manualLoadStrategy_doesNotCallMetadataLoaderLoad() {
+ final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+ final EmojiCompat.Config config = new ValidTestConfig(loader)
+ .setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL);
+
+ EmojiCompat.reset(config);
+
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_DEFAULT, EmojiCompat.get().getLoadState());
+ }
+
private static class ValidTestConfig extends EmojiCompat.Config {
ValidTestConfig() {
super(new TestConfigBuilder.TestEmojiDataLoader());
}
+
+ ValidTestConfig(EmojiCompat.MetadataRepoLoader loader) {
+ super(loader);
+ }
}
}
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiCompatTest.java b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiCompatTest.java
index dd8f3b9..fd9df56 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiCompatTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiCompatTest.java
@@ -53,6 +53,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -691,6 +692,97 @@
metadataLoader.getLoaderLatch().countDown();
}
+ @Test(expected = IllegalStateException.class)
+ public void testLoad_throwsException_whenLoadStrategyDefault() {
+ final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+ final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader);
+ EmojiCompat.reset(config);
+
+ EmojiCompat.get().load();
+ }
+
+ @Test
+ @SdkSuppress(maxSdkVersion = 18)
+ public void testLoad_pre19() {
+ final EmojiCompat.MetadataRepoLoader loader = spy(new TestConfigBuilder
+ .TestEmojiDataLoader());
+ final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
+ .setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL);
+
+ EmojiCompat.reset(config);
+
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_DEFAULT, EmojiCompat.get().getLoadState());
+
+ EmojiCompat.get().load();
+ assertEquals(EmojiCompat.LOAD_STATE_SUCCEEDED, EmojiCompat.get().getLoadState());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 19)
+ public void testLoad_startsLoading() {
+ final EmojiCompat.MetadataRepoLoader loader = spy(new TestConfigBuilder
+ .TestEmojiDataLoader());
+ final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
+ .setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL);
+
+ EmojiCompat.reset(config);
+
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_DEFAULT, EmojiCompat.get().getLoadState());
+
+ EmojiCompat.get().load();
+ verify(loader, times(1)).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_SUCCEEDED, EmojiCompat.get().getLoadState());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 19)
+ public void testLoad_onceSuccessDoesNotStartLoading() {
+ final EmojiCompat.MetadataRepoLoader loader = spy(new TestConfigBuilder
+ .TestEmojiDataLoader());
+ final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
+ .setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL);
+
+ EmojiCompat.reset(config);
+
+ EmojiCompat.get().load();
+ verify(loader, times(1)).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_SUCCEEDED, EmojiCompat.get().getLoadState());
+
+ reset(loader);
+ EmojiCompat.get().load();
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.LOAD_STATE_SUCCEEDED, EmojiCompat.get().getLoadState());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 19)
+ public void testLoad_onceLoadingDoesNotStartLoading() throws InterruptedException {
+ final TestConfigBuilder.WaitingDataLoader loader = spy(
+ new TestConfigBuilder.WaitingDataLoader(true /*success*/));
+ final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
+ .setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL);
+
+ EmojiCompat.reset(config);
+
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+
+ EmojiCompat.get().load();
+ verify(loader, times(1)).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+ assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_LOADING);
+
+ reset(loader);
+
+ EmojiCompat.get().load();
+ verify(loader, never()).load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
+
+ loader.getLoaderLatch().countDown();
+ loader.getTestLatch().await();
+
+ assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED);
+ }
+
@Test
@SdkSuppress(maxSdkVersion = 18)
public void testGetAssetSignature() {
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
index 27ea7c6..2882858 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiInputFilterTest.java
@@ -114,4 +114,16 @@
verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
}
+
+ @Test
+ public void testFilter_withManualLoadStrategy() {
+ final Spannable testString = new SpannableString("abc");
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_DEFAULT);
+
+ final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
+
+ assertNotNull(result);
+ verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+ verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
}
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
index fade6f7..b8f0154 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTextWatcherTest.java
@@ -109,4 +109,15 @@
verify(mEmojiCompat, times(1)).process(any(Spannable.class), anyInt(), anyInt(), anyInt(),
eq(EmojiCompat.REPLACE_STRATEGY_ALL));
}
+
+ @Test
+ public void testFilter_withManualLoadStrategy() {
+ final Spannable testString = new SpannableString("abc");
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_DEFAULT);
+
+ mTextWatcher.onTextChanged(testString, 0, 0, 1);
+
+ verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+ verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
}
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTransformationMethodTest.java b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTransformationMethodTest.java
new file mode 100644
index 0000000..cf74331
--- /dev/null
+++ b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiTransformationMethodTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2018 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 androidx.emoji.widget;
+
+import static junit.framework.TestCase.assertSame;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static androidx.emoji.util.EmojiMatcher.sameCharSequence;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.method.TransformationMethod;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import androidx.emoji.text.EmojiCompat;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiTransformationMethodTest {
+
+ private EmojiTransformationMethod mTransformationMethod;
+ private TransformationMethod mWrappedTransformationMethod;
+ private View mView;
+ private EmojiCompat mEmojiCompat;
+ private final String mTestString = "abc";
+
+ @Before
+ public void setup() {
+ mEmojiCompat = mock(EmojiCompat.class);
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
+ when(mEmojiCompat.process(any(CharSequence.class))).thenAnswer(new Answer<CharSequence>() {
+ @Override
+ public CharSequence answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ return new SpannableString((String) args[0]);
+ }
+ });
+ EmojiCompat.reset(mEmojiCompat);
+
+ mView = mock(View.class);
+ when(mView.isInEditMode()).thenReturn(false);
+
+ mWrappedTransformationMethod = mock(TransformationMethod.class);
+ when(mWrappedTransformationMethod.getTransformation(any(CharSequence.class),
+ any(View.class))).thenAnswer(new Answer<CharSequence>() {
+ @Override
+ public CharSequence answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ return (String) args[0];
+ }
+ });
+
+ mTransformationMethod = new EmojiTransformationMethod(mWrappedTransformationMethod);
+ }
+
+ @Test
+ public void testFilter_withNullSource() {
+ assertNull(mTransformationMethod.getTransformation(null, mView));
+ verify(mEmojiCompat, never()).process(any(CharSequence.class));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testFilter_withNullView() {
+ mTransformationMethod.getTransformation("", null);
+ }
+
+ @Test
+ public void testFilter_withNullTransformationMethod() {
+ mTransformationMethod = new EmojiTransformationMethod(null);
+
+ final CharSequence result = mTransformationMethod.getTransformation(mTestString, mView);
+
+ assertTrue(TextUtils.equals(new SpannableString(mTestString), result));
+ verify(mEmojiCompat, times(1)).process(sameCharSequence(mTestString));
+ }
+
+ @Test
+ public void testFilter() {
+ final CharSequence result = mTransformationMethod.getTransformation(mTestString, mView);
+
+ assertTrue(TextUtils.equals(new SpannableString(mTestString), result));
+ assertTrue(result instanceof Spannable);
+ verify(mWrappedTransformationMethod, times(1)).getTransformation(
+ sameCharSequence(mTestString), same(mView));
+ verify(mEmojiCompat, times(1)).process(sameCharSequence(mTestString));
+ verify(mEmojiCompat, never()).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
+
+ @Test
+ public void testFilter_whenEmojiCompatLoading() {
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_LOADING);
+
+ final CharSequence result = mTransformationMethod.getTransformation(mTestString, mView);
+
+ assertSame(mTestString, result);
+ verify(mWrappedTransformationMethod, times(1)).getTransformation(
+ sameCharSequence(mTestString), same(mView));
+ verify(mEmojiCompat, never()).process(sameCharSequence(mTestString));
+ verify(mEmojiCompat, never()).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
+
+ @Test
+ public void testFilter_whenEmojiCompatLoadFailed() {
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILED);
+
+ final CharSequence result = mTransformationMethod.getTransformation(mTestString, mView);
+
+ assertSame(mTestString, result);
+ verify(mWrappedTransformationMethod, times(1)).getTransformation(
+ sameCharSequence(mTestString), same(mView));
+ verify(mEmojiCompat, never()).process(sameCharSequence(mTestString));
+ verify(mEmojiCompat, never()).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
+
+ @Test
+ public void testFilter_withManualLoadStrategy() {
+ when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_DEFAULT);
+
+ final CharSequence result = mTransformationMethod.getTransformation(mTestString, mView);
+
+ assertSame(mTestString, result);
+ verify(mWrappedTransformationMethod, times(1)).getTransformation(
+ sameCharSequence(mTestString), same(mView));
+ verify(mEmojiCompat, never()).process(sameCharSequence(mTestString));
+ verify(mEmojiCompat, never()).registerInitCallback(any(EmojiCompat.InitCallback.class));
+ }
+}
diff --git a/emoji/core/src/main/java/androidx/emoji/text/EmojiCompat.java b/emoji/core/src/main/java/androidx/emoji/text/EmojiCompat.java
index 31d359d..572606f 100644
--- a/emoji/core/src/main/java/androidx/emoji/text/EmojiCompat.java
+++ b/emoji/core/src/main/java/androidx/emoji/text/EmojiCompat.java
@@ -61,7 +61,9 @@
* <pre><code>EmojiCompat.init(/* a config instance */);</code></pre>
* <p/>
* It is suggested to make the initialization as early as possible in your app. Please check {@link
- * EmojiCompat.Config} for more configuration parameters.
+ * EmojiCompat.Config} for more configuration parameters. Once {@link #init(EmojiCompat.Config)} is
+ * called a singleton instance will be created. Any call after that will not create a new instance
+ * and will return immediately.
* <p/>
* During initialization information about emojis is loaded on a background thread. Before the
* EmojiCompat instance is initialized, calls to functions such as {@link
@@ -96,6 +98,11 @@
"android.support.text.emoji.emojiCompat_replaceAll";
/**
+ * EmojiCompat instance is constructed, however the initialization did not start yet.
+ */
+ public static final int LOAD_STATE_DEFAULT = 3;
+
+ /**
* EmojiCompat is initializing.
*/
public static final int LOAD_STATE_LOADING = 0;
@@ -115,7 +122,7 @@
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
- @IntDef({LOAD_STATE_LOADING, LOAD_STATE_SUCCEEDED, LOAD_STATE_FAILED})
+ @IntDef({LOAD_STATE_DEFAULT, LOAD_STATE_LOADING, LOAD_STATE_SUCCEEDED, LOAD_STATE_FAILED})
@Retention(RetentionPolicy.SOURCE)
public @interface LoadState {
}
@@ -145,6 +152,26 @@
}
/**
+ * {@link EmojiCompat} will start loading metadata when {@link #init(Config)} is called.
+ */
+ public static final int LOAD_STRATEGY_DEFAULT = 0;
+
+ /**
+ * {@link EmojiCompat} will wait for {@link #load()} to be called by developer in order to
+ * start loading metadata.
+ */
+ public static final int LOAD_STRATEGY_MANUAL = 1;
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @IntDef({LOAD_STRATEGY_DEFAULT, LOAD_STRATEGY_MANUAL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LoadStrategy {
+ }
+
+ /**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@@ -205,18 +232,25 @@
private final int mEmojiSpanIndicatorColor;
/**
+ * @see Config#setMetadataLoadStrategy(int)
+ */
+ @LoadStrategy private final int mMetadataLoadStrategy;
+
+ /**
* Private constructor for singleton instance.
*
* @see #init(Config)
*/
private EmojiCompat(@NonNull final Config config) {
mInitLock = new ReentrantReadWriteLock();
+ mLoadState = LOAD_STATE_DEFAULT;
mReplaceAll = config.mReplaceAll;
mUseEmojiAsDefaultStyle = config.mUseEmojiAsDefaultStyle;
mEmojiAsDefaultStyleExceptions = config.mEmojiAsDefaultStyleExceptions;
mEmojiSpanIndicatorEnabled = config.mEmojiSpanIndicatorEnabled;
mEmojiSpanIndicatorColor = config.mEmojiSpanIndicatorColor;
mMetadataLoader = config.mMetadataLoader;
+ mMetadataLoadStrategy = config.mMetadataLoadStrategy;
mMainHandler = new Handler(Looper.getMainLooper());
mInitCallbacks = new ArraySet<>();
if (config.mInitCallbacks != null && !config.mInitCallbacks.isEmpty()) {
@@ -230,7 +264,9 @@
/**
* Initialize the singleton instance with a configuration. When used on devices running API 18
* or below, the singleton instance is immediately moved into {@link #LOAD_STATE_SUCCEEDED}
- * state without loading any metadata.
+ * state without loading any metadata. When called for the first time, the library will create
+ * the singleton instance and any call after that will not create a new instance and return
+ * immediately.
*
* @see EmojiCompat.Config
*/
@@ -304,9 +340,30 @@
}
}
- private void loadMetadata() {
+ /**
+ * When {@link Config#setMetadataLoadStrategy(int)} is set to {@link #LOAD_STRATEGY_MANUAL},
+ * this function starts loading the metadata. Calling the function when
+ * {@link Config#setMetadataLoadStrategy(int)} is {@code not} set to
+ * {@link #LOAD_STRATEGY_MANUAL} will throw an exception. The load will {@code not} start if:
+ * <ul>
+ * <li>the metadata is already loaded successfully and {@link #getLoadState()} is
+ * {@link #LOAD_STATE_SUCCEEDED}.
+ * </li>
+ * <li>a previous load attempt is not finished yet and {@link #getLoadState()} is
+ * {@link #LOAD_STATE_LOADING}.</li>
+ * </ul>
+ *
+ * @throws IllegalStateException when {@link Config#setMetadataLoadStrategy(int)} is not set
+ * to {@link #LOAD_STRATEGY_MANUAL}
+ */
+ public void load() {
+ Preconditions.checkState(mMetadataLoadStrategy == LOAD_STRATEGY_MANUAL,
+ "Set metadataLoadStrategy to LOAD_STRATEGY_MANUAL to execute manual loading");
+ if (isInitialized()) return;
+
mInitLock.writeLock().lock();
try {
+ if (mLoadState == LOAD_STATE_LOADING) return;
mLoadState = LOAD_STATE_LOADING;
} finally {
mInitLock.writeLock().unlock();
@@ -315,6 +372,21 @@
mHelper.loadMetadata();
}
+ private void loadMetadata() {
+ mInitLock.writeLock().lock();
+ try {
+ if (mMetadataLoadStrategy == LOAD_STRATEGY_DEFAULT) {
+ mLoadState = LOAD_STATE_LOADING;
+ }
+ } finally {
+ mInitLock.writeLock().unlock();
+ }
+
+ if (getLoadState() == LOAD_STATE_LOADING) {
+ mHelper.loadMetadata();
+ }
+ }
+
private void onMetadataLoadSuccess() {
final Collection<InitCallback> initCallbacks = new ArrayList<>();
mInitLock.writeLock().lock();
@@ -389,8 +461,8 @@
* Returns loading state of the EmojiCompat instance. When used on devices running API 18 or
* below always returns {@link #LOAD_STATE_SUCCEEDED}.
*
- * @return one of {@link #LOAD_STATE_LOADING}, {@link #LOAD_STATE_SUCCEEDED},
- * {@link #LOAD_STATE_FAILED}
+ * @return one of {@link #LOAD_STATE_DEFAULT}, {@link #LOAD_STATE_LOADING},
+ * {@link #LOAD_STATE_SUCCEEDED}, {@link #LOAD_STATE_FAILED}
*/
public @LoadState int getLoadState() {
mInitLock.readLock().lock();
@@ -805,6 +877,7 @@
private Set<InitCallback> mInitCallbacks;
private boolean mEmojiSpanIndicatorEnabled;
private int mEmojiSpanIndicatorColor = Color.GREEN;
+ @LoadStrategy private int mMetadataLoadStrategy = LOAD_STRATEGY_DEFAULT;
/**
* Default constructor.
@@ -938,6 +1011,21 @@
}
/**
+ * Determines the strategy to start loading the metadata. By default {@link EmojiCompat}
+ * will start loading the metadata during {@link EmojiCompat#init(Config)}. When set to
+ * {@link EmojiCompat#LOAD_STRATEGY_MANUAL}, you should call {@link EmojiCompat#load()} to
+ * initiate metadata loading.
+ *
+ * @param strategy one of {@link EmojiCompat#LOAD_STRATEGY_DEFAULT},
+ * {@link EmojiCompat#LOAD_STRATEGY_MANUAL}
+ *
+ */
+ public Config setMetadataLoadStrategy(@LoadStrategy int strategy) {
+ mMetadataLoadStrategy = strategy;
+ return this;
+ }
+
+ /**
* Returns the {@link MetadataRepoLoader}.
*/
protected final MetadataRepoLoader getMetadataRepoLoader() {
diff --git a/emoji/core/src/main/java/androidx/emoji/widget/EmojiInputFilter.java b/emoji/core/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
index 22837a6..b44e859 100644
--- a/emoji/core/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
+++ b/emoji/core/src/main/java/androidx/emoji/widget/EmojiInputFilter.java
@@ -79,6 +79,7 @@
return source;
case EmojiCompat.LOAD_STATE_LOADING:
+ case EmojiCompat.LOAD_STATE_DEFAULT:
EmojiCompat.get().registerInitCallback(getInitCallback());
return source;
diff --git a/emoji/core/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java b/emoji/core/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
index 5d0e552..2197aff 100644
--- a/emoji/core/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
+++ b/emoji/core/src/main/java/androidx/emoji/widget/EmojiTextWatcher.java
@@ -80,6 +80,7 @@
mEmojiReplaceStrategy);
break;
case EmojiCompat.LOAD_STATE_LOADING:
+ case EmojiCompat.LOAD_STATE_DEFAULT:
EmojiCompat.get().registerInitCallback(getInitCallback());
break;
case EmojiCompat.LOAD_STATE_FAILED:
diff --git a/emoji/core/src/main/java/androidx/emoji/widget/EmojiTransformationMethod.java b/emoji/core/src/main/java/androidx/emoji/widget/EmojiTransformationMethod.java
index c082c83..81fe615 100644
--- a/emoji/core/src/main/java/androidx/emoji/widget/EmojiTransformationMethod.java
+++ b/emoji/core/src/main/java/androidx/emoji/widget/EmojiTransformationMethod.java
@@ -57,6 +57,7 @@
return EmojiCompat.get().process(source);
case EmojiCompat.LOAD_STATE_LOADING:
case EmojiCompat.LOAD_STATE_FAILED:
+ case EmojiCompat.LOAD_STATE_DEFAULT:
default:
break;
}
diff --git a/fragment/ktx/OWNERS b/fragment/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/fragment/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/fragment/ktx/build.gradle b/fragment/ktx/build.gradle
new file mode 100644
index 0000000..52cb265
--- /dev/null
+++ b/fragment/ktx/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ buildTypes {
+ debug {
+ testCoverageEnabled = false // Breaks Kotlin compiler.
+ }
+ }
+}
+
+dependencies {
+ api(project(":fragment"))
+ api(KOTLIN_STDLIB)
+ androidTestImplementation(JUNIT)
+ androidTestImplementation(TRUTH)
+ androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
+ androidTestImplementation(TEST_RULES_TMP, libs.exclude_for_espresso)
+}
+
+supportLibrary {
+ name = "Fragment Kotlin Extensions"
+ publish = true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.FRAGMENT
+ inceptionYear = "2018"
+ description = "Kotlin extensions for 'fragment' artifact"
+}
diff --git a/fragment/ktx/src/androidTest/AndroidManifest.xml b/fragment/ktx/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..0a62e3e
--- /dev/null
+++ b/fragment/ktx/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.fragment.ktx">
+ <application>
+ <activity android:name="androidx.fragment.app.TestActivity"/>
+ </application>
+</manifest>
diff --git a/fragment/ktx/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt b/fragment/ktx/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
new file mode 100644
index 0000000..7097955
--- /dev/null
+++ b/fragment/ktx/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
@@ -0,0 +1,59 @@
+package androidx.fragment.app
+
+import android.support.test.annotation.UiThreadTest
+import android.support.test.filters.MediumTest
+import android.support.test.rule.ActivityTestRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+
+@MediumTest
+class FragmentManagerTest {
+ @get:Rule val activityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+ private val fragmentManager get() = activityRule.activity.supportFragmentManager
+
+ @UiThreadTest
+ @Test fun transaction() {
+ val fragment = TestFragment()
+ fragmentManager.transaction {
+ add(fragment, null)
+ }
+ assertThat(fragmentManager.fragments).doesNotContain(fragment)
+ fragmentManager.executePendingTransactions()
+ assertThat(fragmentManager.fragments).contains(fragment)
+ }
+
+ @UiThreadTest
+ @Test fun transactionNow() {
+ val fragment = TestFragment()
+ fragmentManager.transaction(now = true) {
+ add(fragment, null)
+ }
+ assertThat(fragmentManager.fragments).contains(fragment)
+ }
+
+ @UiThreadTest
+ @Test fun transactionAllowingStateLoss() {
+ // Use a detached FragmentManager to ensure state loss.
+ val fragmentManager = FragmentManagerImpl()
+
+ fragmentManager.transaction(allowStateLoss = true) {
+ add(TestFragment(), null)
+ }
+ assertThat(fragmentManager.fragments).isEmpty()
+ }
+
+ @UiThreadTest
+ @Test fun transactionNowAllowingStateLoss() {
+ // Use a detached FragmentManager to ensure state loss.
+ val fragmentManager = FragmentManagerImpl()
+
+ fragmentManager.transaction(now = true, allowStateLoss = true) {
+ add(TestFragment(), null)
+ }
+ assertThat(fragmentManager.fragments).isEmpty()
+ }
+}
+
+class TestActivity : FragmentActivity()
+class TestFragment : Fragment()
diff --git a/fragment/ktx/src/main/AndroidManifest.xml b/fragment/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cd21eb1
--- /dev/null
+++ b/fragment/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<manifest package="androidx.fragment.ktx"/>
diff --git a/fragment/ktx/src/main/java/androidx/fragment/app/FragmentManager.kt b/fragment/ktx/src/main/java/androidx/fragment/app/FragmentManager.kt
new file mode 100644
index 0000000..d9e8e96
--- /dev/null
+++ b/fragment/ktx/src/main/java/androidx/fragment/app/FragmentManager.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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 androidx.fragment.app
+
+/**
+ * Run [body] in a [FragmentTransaction] which is automatically committed if it completes without
+ * exception.
+ *
+ * One of four commit functions will be used based on the values of `now` and `allowStateLoss`:
+ *
+ * | `now` | `allowStateLoss` | Method |
+ * | ----- | ---------------- | ------------------------------ |
+ * | false | false | `commit()` |
+ * | false | true | `commitAllowingStateLoss()` |
+ * | true | false | `commitNow()` |
+ * | true | true | `commitNowAllowingStateLoss()` |
+ */
+inline fun FragmentManager.transaction(
+ now: Boolean = false,
+ allowStateLoss: Boolean = false,
+ body: FragmentTransaction.() -> Unit
+) {
+ val transaction = beginTransaction()
+ transaction.body()
+ if (now) {
+ if (allowStateLoss) {
+ transaction.commitNowAllowingStateLoss()
+ } else {
+ transaction.commitNow()
+ }
+ } else {
+ if (allowStateLoss) {
+ transaction.commitAllowingStateLoss()
+ } else {
+ transaction.commit()
+ }
+ }
+}
diff --git a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
index 99f47d2..efee065 100644
--- a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
+++ b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
@@ -18,12 +18,6 @@
import static android.support.test.InstrumentationRegistry.getContext;
-import static androidx.heifwriter.HeifWriter.INPUT_MODE_BITMAP;
-import static androidx.heifwriter.HeifWriter.INPUT_MODE_BUFFER;
-import static androidx.heifwriter.HeifWriter.INPUT_MODE_SURFACE;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.media.MediaExtractor;
@@ -34,13 +28,21 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
+
+import static androidx.heifwriter.HeifWriter.INPUT_MODE_BITMAP;
+import static androidx.heifwriter.HeifWriter.INPUT_MODE_BUFFER;
+import static androidx.heifwriter.HeifWriter.INPUT_MODE_SURFACE;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.heifwriter.test.R;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -553,10 +555,14 @@
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(filename);
MediaFormat format = extractor.getTrackFormat(0);
- int gridWidth = format.getInteger(MediaFormat.KEY_GRID_WIDTH);
- int gridHeight = format.getInteger(MediaFormat.KEY_GRID_HEIGHT);
- assertEquals("Wrong grid width", 512, gridWidth);
- assertEquals("Wrong grid height", 512, gridHeight);
+ int gridWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
+ int gridHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
+ int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
+ int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
+ assertTrue("Wrong grid width or cols",
+ ((width + gridWidth - 1) / gridWidth) == gridCols);
+ assertTrue("Wrong grid height or rows",
+ ((height + gridHeight - 1) / gridHeight) == gridRows);
extractor.release();
}
}
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
index 85aa925..babfc18 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
@@ -17,14 +17,9 @@
package androidx.heifwriter;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
-import android.opengl.GLES20;
-import android.opengl.GLUtils;
-import android.os.Looper;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
@@ -32,13 +27,19 @@
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
+import android.opengl.GLES20;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.util.Range;
import android.view.Surface;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -179,25 +180,14 @@
throw new IllegalArgumentException("invalid encoder inputs");
}
- mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
-
- mWidth = width;
- mHeight = height;
-
- if (useGrid) {
- mGridWidth = GRID_WIDTH;
- mGridHeight = GRID_HEIGHT;
- mGridRows = (height + GRID_HEIGHT - 1) / GRID_HEIGHT;
- mGridCols = (width + GRID_WIDTH - 1) / GRID_WIDTH;
- } else {
- mGridWidth = mWidth;
- mGridHeight = mHeight;
- mGridRows = 1;
- mGridCols = 1;
+ boolean useHeicEncoder = false;
+ try {
+ mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
+ useHeicEncoder = true;
+ } catch (Exception e) {
+ mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
}
- mNumTiles = mGridRows * mGridCols;
-
mInputMode = inputMode;
mCallback = cb;
@@ -218,13 +208,61 @@
CodecCapabilities.COLOR_FormatYUV420Flexible;
// TODO: determine how to set bitrate and framerate, or use constant quality
- MediaFormat codecFormat = MediaFormat.createVideoFormat(
- MediaFormat.MIMETYPE_VIDEO_HEVC, mGridWidth, mGridHeight);
+ mWidth = width;
+ mHeight = height;
+
+ int gridWidth, gridHeight, gridRows, gridCols;
+
+ useGrid = useGrid && (width > GRID_WIDTH || height > GRID_HEIGHT);
+
+ if (useGrid) {
+ gridWidth = GRID_WIDTH;
+ gridHeight = GRID_HEIGHT;
+ gridRows = (height + GRID_HEIGHT - 1) / GRID_HEIGHT;
+ gridCols = (width + GRID_WIDTH - 1) / GRID_WIDTH;
+ } else {
+ gridWidth = mWidth;
+ gridHeight = mHeight;
+ gridRows = 1;
+ gridCols = 1;
+ }
+
+ MediaFormat codecFormat;
+ if (useHeicEncoder) {
+ codecFormat = MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC, mWidth, mHeight);
+ } else {
+ codecFormat = MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_VIDEO_HEVC, gridWidth, gridHeight);
+ }
+
+ if (useGrid) {
+ codecFormat.setInteger(MediaFormat.KEY_TILE_WIDTH, gridWidth);
+ codecFormat.setInteger(MediaFormat.KEY_TILE_HEIGHT, gridHeight);
+ codecFormat.setInteger(MediaFormat.KEY_GRID_COLUMNS, gridCols);
+ codecFormat.setInteger(MediaFormat.KEY_GRID_ROWS, gridRows);
+ }
+
+ if (useHeicEncoder) {
+ mGridWidth = width;
+ mGridHeight = height;
+ mGridRows = 1;
+ mGridCols = 1;
+ } else {
+ mGridWidth = gridWidth;
+ mGridHeight = gridHeight;
+ mGridRows = gridRows;
+ mGridCols = gridCols;
+ }
+ mNumTiles = mGridRows * mGridCols;
+
codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
MediaCodecInfo.CodecCapabilities caps =
- mEncoder.getCodecInfo().getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ mEncoder.getCodecInfo().getCapabilitiesForType(useHeicEncoder
+ ? MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC
+ : MediaFormat.MIMETYPE_VIDEO_HEVC);
MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
codecFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mNumTiles);
@@ -262,34 +300,36 @@
if (useSurfaceInternally) {
mEncoderSurface = mEncoder.createInputSurface();
- boolean useGLCopy = (mNumTiles > 1) || (inputMode == INPUT_MODE_BITMAP);
- mEOSTracker = new SurfaceEOSTracker(useGLCopy);
+ boolean copyTiles = (mNumTiles > 1);
+ mEOSTracker = new SurfaceEOSTracker(copyTiles);
- if (useGLCopy) {
- mEncoderEglSurface = new EglWindowSurface(mEncoderSurface);
- mEncoderEglSurface.makeCurrent();
+ if (inputMode == INPUT_MODE_SURFACE) {
+ if (copyTiles) {
+ mEncoderEglSurface = new EglWindowSurface(mEncoderSurface);
+ mEncoderEglSurface.makeCurrent();
- mRectBlt = new EglRectBlt(
- new Texture2dProgram((inputMode == INPUT_MODE_BITMAP) ?
- Texture2dProgram.TEXTURE_2D :
- Texture2dProgram.TEXTURE_EXT),
- mWidth, mHeight);
+ mRectBlt = new EglRectBlt(
+ new Texture2dProgram((inputMode == INPUT_MODE_BITMAP)
+ ? Texture2dProgram.TEXTURE_2D
+ : Texture2dProgram.TEXTURE_EXT),
+ mWidth, mHeight);
- mTextureId = mRectBlt.createTextureObject();
+ mTextureId = mRectBlt.createTextureObject();
- if (inputMode == INPUT_MODE_SURFACE) {
- // use single buffer mode to block on input
- mInputTexture = new SurfaceTexture(mTextureId, true);
- mInputTexture.setOnFrameAvailableListener(this);
- mInputTexture.setDefaultBufferSize(mWidth, mHeight);
- mInputSurface = new Surface(mInputTexture);
+ if (inputMode == INPUT_MODE_SURFACE) {
+ // use single buffer mode to block on input
+ mInputTexture = new SurfaceTexture(mTextureId, true);
+ mInputTexture.setOnFrameAvailableListener(this);
+ mInputTexture.setDefaultBufferSize(mWidth, mHeight);
+ mInputSurface = new Surface(mInputTexture);
+ }
+
+ // make uncurrent since onFrameAvailable could be called on arbituray thread.
+ // making the context current on a different thread will cause error.
+ mEncoderEglSurface.makeUnCurrent();
+ } else {
+ mInputSurface = mEncoderSurface;
}
-
- // make uncurrent since the onFrameAvailable could be called on arbituray thread.
- // making the context current on a different thread will cause error.
- mEncoderEglSurface.makeUnCurrent();
- } else {
- mInputSurface = mEncoderSurface;
}
} else {
for (int i = 0; i < INPUT_BUFFER_POOL_SIZE; i++) {
@@ -321,7 +361,20 @@
computePresentationTime(mInputIndex + mNumTiles - 1));
if (takeFrame) {
- copyTilesGL(mTmpMatrix);
+ // Copies from surface texture to encoder inputs using GL.
+ GLES20.glViewport(0, 0, mGridWidth, mGridHeight);
+
+ for (int row = 0; row < mGridRows; row++) {
+ for (int col = 0; col < mGridCols; col++) {
+ int left = col * mGridWidth;
+ int top = row * mGridHeight;
+ mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
+ mRectBlt.copyRect(mTextureId, mTmpMatrix, mSrcRect);
+ mEncoderEglSurface.setPresentationTime(
+ 1000 * computePresentationTime(mInputIndex++));
+ mEncoderEglSurface.swapBuffers();
+ }
+ }
}
surfaceTexture.releaseTexImage();
@@ -407,21 +460,16 @@
if (!takeFrame) return;
synchronized (this) {
- if (mEncoderEglSurface == null) {
- return;
+ for (int row = 0; row < mGridRows; row++) {
+ for (int col = 0; col < mGridCols; col++) {
+ int left = col * mGridWidth;
+ int top = row * mGridHeight;
+ mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
+ Canvas canvas = mEncoderSurface.lockCanvas(null);
+ canvas.drawBitmap(bitmap, mSrcRect, mDstRect, null);
+ mEncoderSurface.unlockCanvasAndPost(canvas);
+ }
}
-
- mEncoderEglSurface.makeCurrent();
-
- // load the bitmap to texture
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
- GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
-
- copyTilesGL(Texture2dProgram.V_FLIP_MATRIX);
-
- // make uncurrent since the onFrameAvailable could be called on arbituray thread.
- // making the context current on a different thread will cause error.
- mEncoderEglSurface.makeUnCurrent();
}
}
@@ -594,28 +642,6 @@
}
/**
- * Copies from source frame to encoder inputs using GL. The source could be either
- * client's input surface, or the input bitmap loaded to texture.
- *
- * @param texMatrix The texture matrix to use. See the shader program in
- * {@link Texture2dProgram} as well as {@link SurfaceTexture} for more details.
- */
- private void copyTilesGL(float[] texMatrix) {
- GLES20.glViewport(0, 0, mGridWidth, mGridHeight);
-
- for (int row = 0; row < mGridRows; row++) {
- for (int col = 0; col < mGridCols; col++) {
- int left = col * mGridWidth;
- int top = row * mGridHeight;
- mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
- mRectBlt.copyRect(mTextureId, texMatrix, mSrcRect);
- mEncoderEglSurface.setPresentationTime(1000 * computePresentationTime(mInputIndex++));
- mEncoderEglSurface.swapBuffers();
- }
- }
- }
-
- /**
* Routine to release all resources. Must be run on the same looper that
* handles the MediaCodec callbacks.
*/
@@ -677,7 +703,7 @@
private class SurfaceEOSTracker {
private static final boolean DEBUG_EOS = false;
- final boolean mUseGLCopy;
+ final boolean mCopyTiles;
long mInputEOSTimeNs = -1;
long mLastInputTimeNs = -1;
long mEncoderEOSTimeUs = -1;
@@ -685,14 +711,14 @@
long mLastOutputTimeUs = -1;
boolean mSignaled;
- SurfaceEOSTracker(boolean useGLCopy) {
- mUseGLCopy = useGLCopy;
+ SurfaceEOSTracker(boolean copyTiles) {
+ mCopyTiles = copyTiles;
}
synchronized void updateInputEOSTime(long timestampNs) {
if (DEBUG_EOS) Log.d(TAG, "updateInputEOSTime: " + timestampNs);
- if (mUseGLCopy) {
+ if (mCopyTiles) {
if (mInputEOSTimeNs < 0) {
mInputEOSTimeNs = timestampNs;
}
@@ -773,15 +799,18 @@
if (DEBUG) Log.d(TAG, "onOutputFormatChanged: " + format);
- format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
- format.setInteger(MediaFormat.KEY_WIDTH, mWidth);
- format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
+ if (!MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC.equals(
+ format.getString(MediaFormat.KEY_MIME))) {
+ format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
+ format.setInteger(MediaFormat.KEY_WIDTH, mWidth);
+ format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
- if (mNumTiles > 1) {
- format.setInteger(MediaFormat.KEY_GRID_WIDTH, mGridWidth);
- format.setInteger(MediaFormat.KEY_GRID_HEIGHT, mGridHeight);
- format.setInteger(MediaFormat.KEY_GRID_ROWS, mGridRows);
- format.setInteger(MediaFormat.KEY_GRID_COLS, mGridCols);
+ if (mNumTiles > 1) {
+ format.setInteger(MediaFormat.KEY_TILE_WIDTH, mGridWidth);
+ format.setInteger(MediaFormat.KEY_TILE_HEIGHT, mGridHeight);
+ format.setInteger(MediaFormat.KEY_GRID_ROWS, mGridRows);
+ format.setInteger(MediaFormat.KEY_GRID_COLUMNS, mGridCols);
+ }
}
mCallback.onOutputFormatChanged(HeifEncoder.this, format);
@@ -800,8 +829,12 @@
public void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info) {
if (codec != mEncoder || mOutputEOS) return;
- if (DEBUG) Log.d(TAG, "onInputBufferAvailable: " + index + ", time "
- + info.presentationTimeUs + ", size " + info.size + ", flags " + info.flags);
+ if (DEBUG) {
+ Log.d(TAG, "onOutputBufferAvailable: " + index
+ + ", time " + info.presentationTimeUs
+ + ", size " + info.size
+ + ", flags " + info.flags);
+ }
if ((info.size > 0) && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0)) {
ByteBuffer outputBuffer = codec.getOutputBuffer(index);
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
index be7dffb..9c96b6d 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
@@ -400,7 +400,7 @@
try {
int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
- int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLS);
+ int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
mNumTiles = gridRows * gridCols;
} catch (NullPointerException | ClassCastException e) {
mNumTiles = 1;
diff --git a/leanback/src/main/res/values-be/strings.xml b/leanback/src/main/res/values-be/strings.xml
index 2828004..a4038d6 100644
--- a/leanback/src/main/res/values-be/strings.xml
+++ b/leanback/src/main/res/values-be/strings.xml
@@ -21,8 +21,8 @@
<string name="orb_search_action" msgid="7534843523462177008">"Пошук"</string>
<string name="lb_search_bar_hint" msgid="4819380969103509861">"Пошук"</string>
<string name="lb_search_bar_hint_speech" msgid="2795474673510974502">"Прамоўце пошукавы запыт"</string>
- <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"Шукаць тут <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
- <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"Прамоўце пошукавы запыт тут <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+ <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"Шукаць тут: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+ <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"Прамоўце запыт для пошуку тут: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
<string name="lb_control_display_fast_forward_multiplier" msgid="2721825378927619928">"%1$dX"</string>
<string name="lb_control_display_rewind_multiplier" msgid="6173753802428649303">"%1$dX"</string>
<string name="lb_playback_controls_play" msgid="1590369760862605402">"Прайграць"</string>
@@ -53,7 +53,7 @@
<string name="lb_playback_controls_hidden" msgid="619396299825306757">"Элементы кіравання мультымедыя схаваны. Каб паказаць іх, націсніце d-pad"</string>
<string name="lb_guidedaction_finish_title" msgid="7747913934287176843">"Завяршыць"</string>
<string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"Працягнуць"</string>
- <string name="lb_media_player_error" msgid="8748646000835486516">"Код памылкі MediaPlayer %1$d дадаткова %2$d"</string>
+ <string name="lb_media_player_error" msgid="8748646000835486516">"Код памылкі MediaPlayer: %1$d (дадатковы: %2$d)"</string>
<string name="lb_onboarding_get_started" msgid="7674487829030291492">"ПАЧАЦЬ"</string>
<string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"Далей"</string>
</resources>
diff --git a/leanback/src/main/res/values-es/strings.xml b/leanback/src/main/res/values-es/strings.xml
index dc33a6b..5d85c92 100644
--- a/leanback/src/main/res/values-es/strings.xml
+++ b/leanback/src/main/res/values-es/strings.xml
@@ -22,7 +22,7 @@
<string name="lb_search_bar_hint" msgid="4819380969103509861">"Haz una búsqueda"</string>
<string name="lb_search_bar_hint_speech" msgid="2795474673510974502">"Habla para buscar"</string>
<string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"Busca <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
- <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"Busca <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> por voz"</string>
+ <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"Habla para buscar <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
<string name="lb_control_display_fast_forward_multiplier" msgid="2721825378927619928">"%1$dx"</string>
<string name="lb_control_display_rewind_multiplier" msgid="6173753802428649303">"%1$dx"</string>
<string name="lb_playback_controls_play" msgid="1590369760862605402">"Reproducir"</string>
diff --git a/leanback/src/main/res/values-eu/strings.xml b/leanback/src/main/res/values-eu/strings.xml
index 6b7226f..72c433f 100644
--- a/leanback/src/main/res/values-eu/strings.xml
+++ b/leanback/src/main/res/values-eu/strings.xml
@@ -49,8 +49,8 @@
<string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"Desgaitu azpitituluak"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Aktibatu \"Pantaila txiki gainjarri\" modua"</string>
<string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
- <string name="lb_playback_controls_shown" msgid="7794717158616536936">"Multimedia kontrolatzeko aukerak ikusgai"</string>
- <string name="lb_playback_controls_hidden" msgid="619396299825306757">"Ezkutatuta daude multimedia-edukia kontrolatzeko aukerak. Erakusteko, sakatu nabigazio-gurutzea."</string>
+ <string name="lb_playback_controls_shown" msgid="7794717158616536936">"Multimedia-edukia kontrolatzeko aukerak ikusgai"</string>
+ <string name="lb_playback_controls_hidden" msgid="619396299825306757">"Ezkutatuta daude multimedia-edukia kontrolatzeko aukerak. Haiek erakusteko, sakatu nabigazio-gurutzea."</string>
<string name="lb_guidedaction_finish_title" msgid="7747913934287176843">"Amaitu"</string>
<string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"Egin aurrera"</string>
<string name="lb_media_player_error" msgid="8748646000835486516">"MediaPlayer errore-kodea: %1$d (%2$d gehigarria)"</string>
diff --git a/leanback/src/main/res/values-fr-rCA/strings.xml b/leanback/src/main/res/values-fr-rCA/strings.xml
index de3fde5..ad38e94 100644
--- a/leanback/src/main/res/values-fr-rCA/strings.xml
+++ b/leanback/src/main/res/values-fr-rCA/strings.xml
@@ -32,7 +32,7 @@
<string name="lb_playback_controls_rewind" msgid="1412664391757869774">"Retour arrière"</string>
<string name="lb_playback_controls_rewind_multiplier" msgid="8651612807713092781">"Retour rapide à %1$dX"</string>
<string name="lb_playback_controls_skip_next" msgid="4877009494447817003">"Passer à l\'élément suivant"</string>
- <string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"Passer à l\'élément précédent"</string>
+ <string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"Retourner à l\'élément précédent"</string>
<string name="lb_playback_controls_more_actions" msgid="2827883329510404797">"Autres actions"</string>
<string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"Désélectionner la mention « J\'aime »"</string>
<string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"Sélectionner la mention « J\'aime »"</string>
diff --git a/leanback/src/main/res/values-ja/strings.xml b/leanback/src/main/res/values-ja/strings.xml
index 22980f1..96f27e2 100644
--- a/leanback/src/main/res/values-ja/strings.xml
+++ b/leanback/src/main/res/values-ja/strings.xml
@@ -43,8 +43,8 @@
<string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"1 曲をリピート"</string>
<string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"シャッフルを有効にする"</string>
<string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"シャッフルを無効にする"</string>
- <string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"高品質を有効にする"</string>
- <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"高品質を無効にする"</string>
+ <string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"高画質を有効にする"</string>
+ <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"高画質を無効にする"</string>
<string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"クローズド キャプションを有効にする"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"クローズド キャプションを無効にする"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"ピクチャー イン ピクチャー モードに移動"</string>
diff --git a/leanback/src/main/res/values-ko/strings.xml b/leanback/src/main/res/values-ko/strings.xml
index 9eacb53..b1587b9 100644
--- a/leanback/src/main/res/values-ko/strings.xml
+++ b/leanback/src/main/res/values-ko/strings.xml
@@ -40,7 +40,7 @@
<string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"싫어요 선택"</string>
<string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"반복 안함"</string>
<string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"전체 반복"</string>
- <string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"한 항목 반복"</string>
+ <string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"한 개 반복"</string>
<string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"셔플 사용 설정"</string>
<string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"셔플 사용 중지"</string>
<string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"고품질 사용 설정"</string>
diff --git a/leanback/src/main/res/values-mr/strings.xml b/leanback/src/main/res/values-mr/strings.xml
index b01e273..07b4e14 100644
--- a/leanback/src/main/res/values-mr/strings.xml
+++ b/leanback/src/main/res/values-mr/strings.xml
@@ -29,7 +29,7 @@
<!-- String.format failed for translation -->
<!-- no translation found for lb_control_display_rewind_multiplier (6173753802428649303) -->
<skip />
- <string name="lb_playback_controls_play" msgid="1590369760862605402">"खेळा"</string>
+ <string name="lb_playback_controls_play" msgid="1590369760862605402">"प्ले"</string>
<string name="lb_playback_controls_pause" msgid="1769131316742618433">"विराम द्या"</string>
<string name="lb_playback_controls_fast_forward" msgid="8966769845721269304">"पुढे ढकला"</string>
<string name="lb_playback_controls_fast_forward_multiplier" msgid="801276177839339511">"फास्ट फॉरवर्ड %1$d"</string>
@@ -38,10 +38,10 @@
<string name="lb_playback_controls_skip_next" msgid="4877009494447817003">"पुढील वगळा"</string>
<string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"मागील वगळा"</string>
<string name="lb_playback_controls_more_actions" msgid="2827883329510404797">"आणखी क्रिया"</string>
- <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"वर अंगठा निवड रद्द करा"</string>
- <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"वर अंगठा निवडा"</string>
- <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"खाली अंगठा निवड रद्द करा"</string>
- <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"खाली अंगठा निवडा"</string>
+ <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"थंब अप निवड रद्द करा"</string>
+ <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"थंब अप निवडा"</string>
+ <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"थंब डाउन निवड रद्द करा"</string>
+ <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"थंब डाउन निवडा"</string>
<string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"काहीही रिपीट करू नका"</string>
<string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"सर्व रिपीट करा"</string>
<string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"एक रिपीट करा"</string>
diff --git a/leanback/src/main/res/values-uz/strings.xml b/leanback/src/main/res/values-uz/strings.xml
index 50a86af..95e0b67 100644
--- a/leanback/src/main/res/values-uz/strings.xml
+++ b/leanback/src/main/res/values-uz/strings.xml
@@ -21,8 +21,8 @@
<string name="orb_search_action" msgid="7534843523462177008">"Qidiruv amali"</string>
<string name="lb_search_bar_hint" msgid="4819380969103509861">"Qidiruv"</string>
<string name="lb_search_bar_hint_speech" msgid="2795474673510974502">"Qidirish uchun gapiring"</string>
- <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"Qidirish: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
- <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"Qidirish uchun ayting: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+ <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ichidan qidirish"</string>
+ <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ichidan qidirish uchun gapiring"</string>
<string name="lb_control_display_fast_forward_multiplier" msgid="2721825378927619928">"%1$dX"</string>
<string name="lb_control_display_rewind_multiplier" msgid="6173753802428649303">"%1$dX"</string>
<string name="lb_playback_controls_play" msgid="1590369760862605402">"Ijro"</string>
@@ -39,14 +39,14 @@
<string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"Salbiy baho tanlovini bekor qilish"</string>
<string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"Salbiy bahoni tanlash"</string>
<string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"Takrorlamaslik"</string>
- <string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"Barchasini takrorlash"</string>
+ <string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"Hammasini takrorlash"</string>
<string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"Bir marta takrorlash"</string>
<string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"Aralashtirish funksiyasini yoqish"</string>
- <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"Aralashtirish funksiyasini o‘chirish"</string>
+ <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"Aralashtirmaslik"</string>
<string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"Yuqori sifatni yoqish"</string>
- <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"Yuqori sifatni o‘chirib qo‘yish"</string>
+ <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"Yuqori sifatda ijro qilmaslik"</string>
<string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"Taglavhalarni yoqish"</string>
- <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"Taglavhalarni o‘chirib qo‘yish"</string>
+ <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"Taglavhalarni chiqarmaslik"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Tasvir ustida tasvir rejimiga kirish"</string>
<string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
<string name="lb_playback_controls_shown" msgid="7794717158616536936">"Boshqaruv elementlari ochiq"</string>
diff --git a/leanback/src/main/res/values-zh-rTW/strings.xml b/leanback/src/main/res/values-zh-rTW/strings.xml
index bcaf11e..9aea4d2 100644
--- a/leanback/src/main/res/values-zh-rTW/strings.xml
+++ b/leanback/src/main/res/values-zh-rTW/strings.xml
@@ -34,10 +34,10 @@
<string name="lb_playback_controls_skip_next" msgid="4877009494447817003">"跳至下一個項目"</string>
<string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"跳至上一個項目"</string>
<string name="lb_playback_controls_more_actions" msgid="2827883329510404797">"更多動作"</string>
- <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"取消選取喜歡"</string>
- <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"選取喜歡"</string>
- <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"取消選取不喜歡"</string>
- <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"選取不喜歡"</string>
+ <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"取消選取「喜歡」"</string>
+ <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"選取「喜歡」"</string>
+ <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"取消選取「不喜歡」"</string>
+ <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"選取「不喜歡」"</string>
<string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"不重複播放"</string>
<string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"重複播放所有項目"</string>
<string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"重複播放單一項目"</string>
diff --git a/lifecycle/reactivestreams/build.gradle b/lifecycle/reactivestreams/build.gradle
index 34390b7..649c526 100644
--- a/lifecycle/reactivestreams/build.gradle
+++ b/lifecycle/reactivestreams/build.gradle
@@ -35,9 +35,7 @@
testImplementation(JUNIT)
testImplementation(RX_JAVA)
- testImplementation(TEST_RUNNER)
-
- androidTestImplementation(SUPPORT_APPCOMPAT, libs.support_exclude_config)
+ testImplementation(project(":arch:core-testing"))
}
supportLibrary {
@@ -48,4 +46,4 @@
inceptionYear = "2017"
description = "Android Lifecycle Reactivestreams"
url = SupportLibraryExtension.ARCHITECTURE_URL
-}
\ No newline at end of file
+}
diff --git a/lifecycle/reactivestreams/ktx/OWNERS b/lifecycle/reactivestreams/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/lifecycle/reactivestreams/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/lifecycle/reactivestreams/ktx/build.gradle b/lifecycle/reactivestreams/ktx/build.gradle
new file mode 100644
index 0000000..2604a8e
--- /dev/null
+++ b/lifecycle/reactivestreams/ktx/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ api(project(":lifecycle:lifecycle-reactivestreams"))
+ api(KOTLIN_STDLIB)
+
+ testImplementation(JUNIT)
+ testImplementation(RX_JAVA)
+ testImplementation(TRUTH)
+ testImplementation(project(":arch:core-testing"))
+}
+
+supportLibrary {
+ name = "Android Lifecycle ReactiveStreams KTX"
+ publish = true
+ mavenVersion = LibraryVersions.LIFECYCLES_EXT
+ mavenGroup = LibraryGroups.LIFECYCLE
+ inceptionYear = "2018"
+ description = "Kotlin extensions for Lifecycle ReactiveStreams"
+ url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/lifecycle/reactivestreams/ktx/src/main/AndroidManifest.xml b/lifecycle/reactivestreams/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d2c150c
--- /dev/null
+++ b/lifecycle/reactivestreams/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest package="androidx.lifecycle.reactivestreams.ktx"/>
diff --git a/lifecycle/reactivestreams/ktx/src/main/java/androidx/lifecycle/LiveDataReactiveSteams.kt b/lifecycle/reactivestreams/ktx/src/main/java/androidx/lifecycle/LiveDataReactiveSteams.kt
new file mode 100644
index 0000000..e4a95e7
--- /dev/null
+++ b/lifecycle/reactivestreams/ktx/src/main/java/androidx/lifecycle/LiveDataReactiveSteams.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.lifecycle
+
+import org.reactivestreams.Publisher
+
+/**
+ * Adapts the given [LiveData] stream to a ReactiveStreams [Publisher].
+ *
+ * @see LiveDataReactiveStreams.toPublisher
+ */
+inline fun <T> LiveData<T>.toPublisher(lifecycle: LifecycleOwner): Publisher<T> =
+ LiveDataReactiveStreams.toPublisher(lifecycle, this)
+
+/**
+ * Creates an observable [LiveData] stream from a ReactiveStreams [Publisher].
+ *
+ * @see LiveDataReactiveStreams.fromPublisher
+ */
+inline fun <T> Publisher<T>.toLiveData(): LiveData<T> =
+ LiveDataReactiveStreams.fromPublisher(this)
diff --git a/lifecycle/reactivestreams/ktx/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.kt b/lifecycle/reactivestreams/ktx/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.kt
new file mode 100644
index 0000000..ad3bdb2
--- /dev/null
+++ b/lifecycle/reactivestreams/ktx/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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 androidx.lifecycle
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.google.common.truth.Truth.assertThat
+import io.reactivex.processors.PublishProcessor
+import io.reactivex.processors.ReplayProcessor
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class LiveDataReactiveStreamsTest {
+ @get:Rule val rule = InstantTaskExecutorRule()
+
+ private lateinit var lifecycleOwner: LifecycleOwner
+
+ @Before fun init() {
+ lifecycleOwner = object : LifecycleOwner {
+ internal var registry = LifecycleRegistry(this)
+
+ init {
+ registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ }
+
+ override fun getLifecycle(): Lifecycle {
+ return registry
+ }
+ }
+ }
+
+ @Test fun convertsFromPublisher() {
+ val processor = PublishProcessor.create<String>()
+ val liveData = processor.toLiveData()
+
+ val output = mutableListOf<String?>()
+ liveData.observe(lifecycleOwner, Observer { output.add(it) })
+
+ processor.onNext("foo")
+ processor.onNext("bar")
+ processor.onNext("baz")
+
+ assertThat(output).containsExactly("foo", "bar", "baz")
+ }
+
+ @Test fun convertsToPublisherWithSyncData() {
+ val liveData = MutableLiveData<String>()
+ liveData.value = "foo"
+
+ val outputProcessor = ReplayProcessor.create<String>()
+ liveData.toPublisher(lifecycleOwner).subscribe(outputProcessor)
+
+ liveData.value = "bar"
+ liveData.value = "baz"
+
+ assertThat(outputProcessor.values).asList().containsExactly("foo", "bar", "baz")
+ }
+}
diff --git a/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml b/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 304fd93..0000000
--- a/lifecycle/reactivestreams/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2017 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="androidx.lifecycle.reactivestreams.test">
- <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
-
- <application>
- <activity android:name="androidx.lifecycle.viewmodeltest.ViewModelActivity"
- android:theme="@style/Base.Theme.AppCompat">
- </activity>
- </application>
-
-</manifest>
diff --git a/lifecycle/reactivestreams/src/main/java/androidx/lifecycle/LiveDataReactiveStreams.java b/lifecycle/reactivestreams/src/main/java/androidx/lifecycle/LiveDataReactiveStreams.java
index 4af5a09..711e71e 100644
--- a/lifecycle/reactivestreams/src/main/java/androidx/lifecycle/LiveDataReactiveStreams.java
+++ b/lifecycle/reactivestreams/src/main/java/androidx/lifecycle/LiveDataReactiveStreams.java
@@ -165,7 +165,7 @@
}
/**
- * Creates an Observable {@link LiveData} stream from a ReactiveStreams publisher.
+ * Creates an observable {@link LiveData} stream from a ReactiveStreams {@link Publisher}}.
*
* <p>
* When the LiveData becomes active, it subscribes to the emissions from the Publisher.
diff --git a/lifecycle/reactivestreams/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.java b/lifecycle/reactivestreams/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.java
index 6311d94..ed792d2 100644
--- a/lifecycle/reactivestreams/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.java
+++ b/lifecycle/reactivestreams/src/test/java/androidx/lifecycle/LiveDataReactiveStreamsTest.java
@@ -21,15 +21,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import android.support.test.filters.SmallTest;
-
import androidx.annotation.Nullable;
-import androidx.arch.core.executor.ArchTaskExecutor;
-import androidx.arch.core.executor.TaskExecutor;
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
@@ -47,8 +45,9 @@
import io.reactivex.schedulers.TestScheduler;
import io.reactivex.subjects.AsyncSubject;
-@SmallTest
public class LiveDataReactiveStreamsTest {
+ @Rule public final TestRule instantTaskExecutorRule = new InstantTaskExecutorRule();
+
private LifecycleOwner mLifecycleOwner;
private final List<String> mLiveDataOutput = new ArrayList<>();
@@ -62,7 +61,6 @@
private final ReplayProcessor<String> mOutputProcessor = ReplayProcessor.create();
private static final TestScheduler sBackgroundScheduler = new TestScheduler();
- private Thread mTestThread;
@Before
public void init() {
@@ -77,31 +75,6 @@
return mRegistry;
}
};
- mTestThread = Thread.currentThread();
- ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
-
- @Override
- public void executeOnDiskIO(Runnable runnable) {
- throw new IllegalStateException();
- }
-
- @Override
- public void postToMainThread(Runnable runnable) {
- // Wrong implementation, but it is fine for test
- runnable.run();
- }
-
- @Override
- public boolean isMainThread() {
- return Thread.currentThread() == mTestThread;
- }
-
- });
- }
-
- @After
- public void removeExecutorDelegate() {
- ArchTaskExecutor.getInstance().setDelegate(null);
}
@Test
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
index 227afad..ba89f99 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
@@ -68,6 +68,7 @@
import android.os.Build;
import android.os.Bundle;
import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -537,6 +538,7 @@
@Test
@MediumTest
+ @FlakyTest(bugId = 74093976)
public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
connectMediaBrowserService();
final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
diff --git a/media/version-compat-tests/previous/client/build.gradle b/media/version-compat-tests/previous/client/build.gradle
index 8b0866e..06e446f 100644
--- a/media/version-compat-tests/previous/client/build.gradle
+++ b/media/version-compat-tests/previous/client/build.gradle
@@ -22,7 +22,7 @@
dependencies {
androidTestImplementation project(':support-media-compat-test-lib')
- androidTestImplementation "com.android.support:support-media-compat:27.0.1"
+ androidTestImplementation "com.android.support:support-media-compat:27.1.0"
androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
}
diff --git a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
index d65b2bc..891769a 100644
--- a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
+++ b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
@@ -64,6 +64,7 @@
import android.os.Build;
import android.os.Bundle;
import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -489,6 +490,7 @@
@Test
@MediumTest
+ @FlakyTest(bugId = 74093976)
public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
connectMediaBrowserService();
final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
diff --git a/media/version-compat-tests/previous/service/build.gradle b/media/version-compat-tests/previous/service/build.gradle
index 3e1d9b9..5ac5270 100644
--- a/media/version-compat-tests/previous/service/build.gradle
+++ b/media/version-compat-tests/previous/service/build.gradle
@@ -22,7 +22,7 @@
dependencies {
androidTestImplementation(project(":support-media-compat-test-lib"))
- androidTestImplementation "com.android.support:support-media-compat:27.0.1"
+ androidTestImplementation "com.android.support:support-media-compat:27.1.0"
androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
}
diff --git a/palette/ktx/OWNERS b/palette/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/palette/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/palette/ktx/build.gradle b/palette/ktx/build.gradle
new file mode 100644
index 0000000..e5d5d43
--- /dev/null
+++ b/palette/ktx/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ buildTypes {
+ debug {
+ testCoverageEnabled = false // Breaks Kotlin compiler.
+ }
+ }
+}
+
+dependencies {
+ api(project(":palette"))
+ api(KOTLIN_STDLIB)
+ androidTestImplementation(JUNIT)
+ androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
+}
+
+supportLibrary {
+ name = "Palette Kotlin Extensions"
+ publish = true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.PALETTE
+ inceptionYear = "2018"
+ description = "Kotlin extensions for 'palette' artifact"
+}
diff --git a/palette/ktx/src/androidTest/java/androidx/palette/graphics/PaletteTest.kt b/palette/ktx/src/androidTest/java/androidx/palette/graphics/PaletteTest.kt
new file mode 100644
index 0000000..c108493
--- /dev/null
+++ b/palette/ktx/src/androidTest/java/androidx/palette/graphics/PaletteTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 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 androidx.palette.graphics
+
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.ARGB_8888
+import android.graphics.Canvas
+import android.graphics.Color.RED
+import androidx.palette.graphics.Target.VIBRANT
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class PaletteTest {
+ @Test fun bitmapBuild() {
+ val bitmap = Bitmap.createBitmap(10, 10, ARGB_8888)
+ // There's no easy way to test that the palette was created from our Bitmap.
+ assertNotNull(bitmap.buildPalette())
+ }
+
+ @Test fun operatorGet() {
+ val bitmap = Bitmap.createBitmap(10, 10, ARGB_8888).apply {
+ Canvas(this).drawColor(RED)
+ }
+ val palette = Palette.from(bitmap).generate()
+ assertSame(palette.getSwatchForTarget(VIBRANT), palette[VIBRANT])
+ }
+}
diff --git a/palette/ktx/src/main/AndroidManifest.xml b/palette/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f25fda5
--- /dev/null
+++ b/palette/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2018 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.
+ -->
+<manifest package="androidx.palette.ktx"/>
diff --git a/palette/ktx/src/main/java/androidx/palette/graphics/Palette.kt b/palette/ktx/src/main/java/androidx/palette/graphics/Palette.kt
new file mode 100644
index 0000000..58da09a
--- /dev/null
+++ b/palette/ktx/src/main/java/androidx/palette/graphics/Palette.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.palette.graphics
+
+import android.graphics.Bitmap
+
+/**
+ * Create a [Palette.Builder] from this bitmap.
+ *
+ * @see Palette.from
+ */
+inline fun Bitmap.buildPalette() = Palette.Builder(this)
+
+/**
+ * Returns the selected swatch for the given target from the palette, or `null` if one
+ * could not be found.
+ *
+ * @see Palette.getSwatchForTarget
+ */
+inline operator fun Palette.get(target: Target): Palette.Swatch? = getSwatchForTarget(target)
diff --git a/persistence/db/ktx/OWNERS b/persistence/db/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/persistence/db/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/persistence/db/ktx/build.gradle b/persistence/db/ktx/build.gradle
new file mode 100644
index 0000000..676445b
--- /dev/null
+++ b/persistence/db/ktx/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ api(project(":sqlite:sqlite"))
+ api(KOTLIN_STDLIB)
+
+ testImplementation(JUNIT)
+ testImplementation(MOCKITO_CORE)
+}
+
+supportLibrary {
+ name = "Android DB KTX"
+ publish = true
+ mavenVersion = LibraryVersions.ROOM
+ mavenGroup = LibraryGroups.PERSISTENCE
+ inceptionYear = "2018"
+ description = "Kotlin extensions for DB"
+ url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/persistence/db/ktx/src/main/AndroidManifest.xml b/persistence/db/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f4d66ba
--- /dev/null
+++ b/persistence/db/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest package="androidx.sqlite.db.ktx"/>
diff --git a/persistence/db/ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt b/persistence/db/ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt
new file mode 100644
index 0000000..ba7b338
--- /dev/null
+++ b/persistence/db/ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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 androidx.sqlite.db
+
+/**
+ * Run [body] in a transaction marking it as successful if it completes without exception.
+ *
+ * @param exclusive Run in `EXCLUSIVE` mode when true, `IMMEDIATE` mode otherwise.
+ */
+inline fun <T> SupportSQLiteDatabase.transaction(
+ exclusive: Boolean = true,
+ body: SupportSQLiteDatabase.() -> T
+): T {
+ if (exclusive) {
+ beginTransaction()
+ } else {
+ beginTransactionNonExclusive()
+ }
+ try {
+ val result = body()
+ setTransactionSuccessful()
+ return result
+ } finally {
+ endTransaction()
+ }
+}
diff --git a/persistence/db/ktx/src/test/java/androidx/sqlite/db/SupportSQLiteDatabaseTest.kt b/persistence/db/ktx/src/test/java/androidx/sqlite/db/SupportSQLiteDatabaseTest.kt
new file mode 100644
index 0000000..76698d3
--- /dev/null
+++ b/persistence/db/ktx/src/test/java/androidx/sqlite/db/SupportSQLiteDatabaseTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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 androidx.sqlite.db
+
+import org.junit.Assert.fail
+import org.junit.Test
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+class SupportSQLiteDatabaseTest {
+ @Test fun exclusiveDefault() {
+ val db = mock(SupportSQLiteDatabase::class.java)
+ db.transaction {}
+ verify(db).beginTransaction()
+ }
+
+ @Test fun exclusiveFalse() {
+ val db = mock(SupportSQLiteDatabase::class.java)
+ db.transaction(exclusive = false) {}
+ verify(db).beginTransactionNonExclusive()
+ }
+
+ @Test fun exclusiveTrue() {
+ val db = mock(SupportSQLiteDatabase::class.java)
+ db.transaction(exclusive = true) {}
+ verify(db).beginTransaction()
+ }
+
+ @Test fun bodyNormalCallsSuccessAndEnd() {
+ val db = mock(SupportSQLiteDatabase::class.java)
+ db.transaction {}
+ verify(db).setTransactionSuccessful()
+ verify(db).endTransaction()
+ }
+
+ @Suppress("UNREACHABLE_CODE") // A programming error might not invoke the lambda.
+ @Test fun bodyThrowsDoesNotCallSuccess() {
+ val db = mock(SupportSQLiteDatabase::class.java)
+ try {
+ db.transaction {
+ throw IllegalStateException()
+ }
+ fail()
+ } catch (e: IllegalStateException) {
+ }
+ verify(db, times(0)).setTransactionSuccessful()
+ verify(db).endTransaction()
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 61676b0..65ef3f4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -40,9 +40,11 @@
includeProject(":car", "car")
includeProject(":cardview", "cardview")
includeProject(":collection", "collection")
+includeProject(":collection-ktx", "collection/ktx")
includeProject(":contentpaging", "content")
includeProject(":coordinatorlayout", "coordinatorlayout")
includeProject(":core", "compat")
+includeProject(":core-ktx", "core/ktx")
includeProject(":cursoradapter", "cursoradapter")
includeProject(":customview", "customview")
includeProject(":documentfile", "documentfile")
@@ -68,6 +70,7 @@
includeProject(":media", "media")
includeProject(":mediarouter", "mediarouter")
includeProject(":palette", "palette")
+includeProject(":palette-ktx", "palette/ktx")
includeProject(":percentlayout", "percent")
includeProject(":preference", "preference")
includeProject(":print", "print")
@@ -78,6 +81,7 @@
includeProject(":slices-view", "slices/view")
includeProject(":slices-builders", "slices/builders")
includeProject(":slidingpanelayout", "slidingpanelayout")
+includeProject(":fragment-ktx", "fragment/ktx")
includeProject(":swiperefreshlayout", "swiperefreshlayout")
includeProject(":textclassifier", "textclassifier")
includeProject(":transition", "transition")
@@ -136,6 +140,7 @@
/////////////////////////////
includeProject(":internal-testutils", "testutils")
+includeProject(":internal-testutils-ktx", "testutils-ktx")
/////////////////////////////
//
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
index 040baf6..f322a19 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
@@ -19,7 +19,6 @@
import static androidx.slice.SliceConvert.unwrap;
import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
-import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
import android.content.Context;
import android.content.Intent;
@@ -43,12 +42,6 @@
private final android.app.slice.SliceManager mManager;
private final List<SliceSpec> mSpecs;
- private final SliceManager.SliceCallback mCallback = new SliceManager.SliceCallback() {
- @Override
- public void onSliceUpdated(@NonNull android.app.slice.Slice s) {
-
- }
- };
SliceManagerWrapper(Context context) {
this(context, context.getSystemService(android.app.slice.SliceManager.class));
@@ -62,12 +55,12 @@
@Override
public void pinSlice(@NonNull Uri uri) {
- mManager.registerSliceCallback(uri, mSpecs, mCallback);
+ mManager.pinSlice(uri, mSpecs);
}
@Override
public void unpinSlice(@NonNull Uri uri) {
- mManager.unregisterSliceCallback(uri, mCallback);
+ mManager.unpinSlice(uri);
}
@Override
@@ -92,8 +85,6 @@
@Nullable
@Override
public Uri mapIntentToUri(@NonNull Intent intent) {
- // TODO: Switch over to mapIntentToUri once it lands in prebuilt.
- Slice slice = bindSlice(intent);
- return slice != null ? slice.getUri() : null;
+ return mManager.mapIntentToUri(intent);
}
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java b/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
index 2ffc02c..8639893 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
@@ -63,8 +63,7 @@
/**
* Produces an {@link LiveData} that tracks a Slice for a given Uri. To use
- * this method your app must have the permission to the slice Uri or hold
- * {@link android.Manifest.permission#BIND_SLICE}).
+ * this method your app must have the permission to the slice Uri.
*/
public static LiveData<Slice> fromUri(Context context, Uri uri) {
return new SliceLiveDataImpl(context.getApplicationContext(), uri);
@@ -72,8 +71,7 @@
/**
* Produces an {@link LiveData} that tracks a Slice for a given Intent. To use
- * this method your app must have the permission to the slice Uri or hold
- * {@link android.Manifest.permission#BIND_SLICE}).
+ * this method your app must have the permission to the slice Uri.
*/
public static LiveData<Slice> fromIntent(@NonNull Context context, @NonNull Intent intent) {
return new SliceLiveDataImpl(context.getApplicationContext(), intent);
diff --git a/slices/view/src/main/res/values-af/strings.xml b/slices/view/src/main/res/values-af/strings.xml
index ea5ab25..812f811 100644
--- a/slices/view/src/main/res/values-af/strings.xml
+++ b/slices/view/src/main/res/values-af/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Meer"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Wys meer"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-am/strings.xml b/slices/view/src/main/res/values-am/strings.xml
index ea5ab25..86c2c4e 100644
--- a/slices/view/src/main/res/values-am/strings.xml
+++ b/slices/view/src/main/res/values-am/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ተጨማሪ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ተጨማሪ አሳይ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ar/strings.xml b/slices/view/src/main/res/values-ar/strings.xml
index 21aa8a4..3acde5d 100644
--- a/slices/view/src/main/res/values-ar/strings.xml
+++ b/slices/view/src/main/res/values-ar/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"بالإضافة إلى <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"المزيد"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"عرض المزيد"</string>
</resources>
diff --git a/slices/view/src/main/res/values-az/strings.xml b/slices/view/src/main/res/values-az/strings.xml
index ea5ab25..8ff3908 100644
--- a/slices/view/src/main/res/values-az/strings.xml
+++ b/slices/view/src/main/res/values-az/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Digər"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Digərinə baxın"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-b+sr+Latn/strings.xml b/slices/view/src/main/res/values-b+sr+Latn/strings.xml
index ffd9b9b..22e42e7 100644
--- a/slices/view/src/main/res/values-b+sr+Latn/strings.xml
+++ b/slices/view/src/main/res/values-b+sr+Latn/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"i još <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Još"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
</resources>
diff --git a/slices/view/src/main/res/values-be/strings.xml b/slices/view/src/main/res/values-be/strings.xml
index df8e965..6ea856b 100644
--- a/slices/view/src/main/res/values-be/strings.xml
+++ b/slices/view/src/main/res/values-be/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Яшчэ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Яшчэ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Яшчэ"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-bg/strings.xml b/slices/view/src/main/res/values-bg/strings.xml
index ea5ab25..882def9 100644
--- a/slices/view/src/main/res/values-bg/strings.xml
+++ b/slices/view/src/main/res/values-bg/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Още"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Показване на още"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-bn/strings.xml b/slices/view/src/main/res/values-bn/strings.xml
index 8737d8c..eb83395 100644
--- a/slices/view/src/main/res/values-bn/strings.xml
+++ b/slices/view/src/main/res/values-bn/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>টি"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"আরও"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"আরও দেখুন"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-bs/strings.xml b/slices/view/src/main/res/values-bs/strings.xml
index ea5ab25..693f885 100644
--- a/slices/view/src/main/res/values-bs/strings.xml
+++ b/slices/view/src/main/res/values-bs/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Više"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ca/strings.xml b/slices/view/src/main/res/values-ca/strings.xml
index 8ae6f78..4ac9c6d 100644
--- a/slices/view/src/main/res/values-ca/strings.xml
+++ b/slices/view/src/main/res/values-ca/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> més"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Més"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostra\'n més"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-cs/strings.xml b/slices/view/src/main/res/values-cs/strings.xml
index 1d39c0f..278810c 100644
--- a/slices/view/src/main/res/values-cs/strings.xml
+++ b/slices/view/src/main/res/values-cs/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"a ještě <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Více"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Zobrazit více"</string>
</resources>
diff --git a/slices/view/src/main/res/values-da/strings.xml b/slices/view/src/main/res/values-da/strings.xml
index 40de41d..806a5fe 100644
--- a/slices/view/src/main/res/values-da/strings.xml
+++ b/slices/view/src/main/res/values-da/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> mere"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mere"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Se mere"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-de/strings.xml b/slices/view/src/main/res/values-de/strings.xml
index 9d9bede..f6a1e32 100644
--- a/slices/view/src/main/res/values-de/strings.xml
+++ b/slices/view/src/main/res/values-de/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mehr"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mehr anzeigen"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-el/strings.xml b/slices/view/src/main/res/values-el/strings.xml
index ea5ab25..19963f2 100644
--- a/slices/view/src/main/res/values-el/strings.xml
+++ b/slices/view/src/main/res/values-el/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Περισσότ."</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Εμφάνιση περισσότερων"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-en-rAU/strings.xml b/slices/view/src/main/res/values-en-rAU/strings.xml
index ea5ab25..f222ba5 100644
--- a/slices/view/src/main/res/values-en-rAU/strings.xml
+++ b/slices/view/src/main/res/values-en-rAU/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-en-rCA/strings.xml b/slices/view/src/main/res/values-en-rCA/strings.xml
index ea5ab25..f222ba5 100644
--- a/slices/view/src/main/res/values-en-rCA/strings.xml
+++ b/slices/view/src/main/res/values-en-rCA/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-en-rGB/strings.xml b/slices/view/src/main/res/values-en-rGB/strings.xml
index ea5ab25..f222ba5 100644
--- a/slices/view/src/main/res/values-en-rGB/strings.xml
+++ b/slices/view/src/main/res/values-en-rGB/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-en-rIN/strings.xml b/slices/view/src/main/res/values-en-rIN/strings.xml
index ea5ab25..f222ba5 100644
--- a/slices/view/src/main/res/values-en-rIN/strings.xml
+++ b/slices/view/src/main/res/values-en-rIN/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-en-rXC/strings.xml b/slices/view/src/main/res/values-en-rXC/strings.xml
index b793412..243dccb 100644
--- a/slices/view/src/main/res/values-en-rXC/strings.xml
+++ b/slices/view/src/main/res/values-en-rXC/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-es-rUS/strings.xml b/slices/view/src/main/res/values-es-rUS/strings.xml
index a445505..7fc5b56 100644
--- a/slices/view/src/main/res/values-es-rUS/strings.xml
+++ b/slices/view/src/main/res/values-es-rUS/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> más"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Más"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar más"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-es/strings.xml b/slices/view/src/main/res/values-es/strings.xml
index dcdd33e..b81e23c 100644
--- a/slices/view/src/main/res/values-es/strings.xml
+++ b/slices/view/src/main/res/values-es/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> más"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Más"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Ver más"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-et/strings.xml b/slices/view/src/main/res/values-et/strings.xml
index a1675ff..80bec54 100644
--- a/slices/view/src/main/res/values-et/strings.xml
+++ b/slices/view/src/main/res/values-et/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"ja veel <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Rohkem"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Kuva rohkem"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-eu/strings.xml b/slices/view/src/main/res/values-eu/strings.xml
index 1c79d4a..295f184 100644
--- a/slices/view/src/main/res/values-eu/strings.xml
+++ b/slices/view/src/main/res/values-eu/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Beste <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Gehiago"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Erakutsi gehiago"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-fa/strings.xml b/slices/view/src/main/res/values-fa/strings.xml
index 2b2abd2..3ef8f85 100644
--- a/slices/view/src/main/res/values-fa/strings.xml
+++ b/slices/view/src/main/res/values-fa/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"بیشتر"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"نمایش موارد بیشتر"</string>
</resources>
diff --git a/slices/view/src/main/res/values-fi/strings.xml b/slices/view/src/main/res/values-fi/strings.xml
index ea5ab25..0e37b9d 100644
--- a/slices/view/src/main/res/values-fi/strings.xml
+++ b/slices/view/src/main/res/values-fi/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Lisää"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Näytä lisää"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-fr-rCA/strings.xml b/slices/view/src/main/res/values-fr-rCA/strings.xml
index ea5ab25..cc10117 100644
--- a/slices/view/src/main/res/values-fr-rCA/strings.xml
+++ b/slices/view/src/main/res/values-fr-rCA/strings.xml
@@ -17,5 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Plus"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Plus"</string>
</resources>
diff --git a/slices/view/src/main/res/values-fr/strings.xml b/slices/view/src/main/res/values-fr/strings.xml
index 73685ce..b4f147b 100644
--- a/slices/view/src/main/res/values-fr/strings.xml
+++ b/slices/view/src/main/res/values-fr/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> autres"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Plus"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Afficher plus"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-gl/strings.xml b/slices/view/src/main/res/values-gl/strings.xml
index 573eae9..f8686de 100644
--- a/slices/view/src/main/res/values-gl/strings.xml
+++ b/slices/view/src/main/res/values-gl/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> máis"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Máis"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Amosar máis"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-gu/strings.xml b/slices/view/src/main/res/values-gu/strings.xml
index ea5ab25..4cf8b47 100644
--- a/slices/view/src/main/res/values-gu/strings.xml
+++ b/slices/view/src/main/res/values-gu/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"વધુ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"વધુ બતાવો"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-hi/strings.xml b/slices/view/src/main/res/values-hi/strings.xml
index ea5ab25..3688497 100644
--- a/slices/view/src/main/res/values-hi/strings.xml
+++ b/slices/view/src/main/res/values-hi/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ज़्यादा देखें"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ज़्यादा देखें"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-hr/strings.xml b/slices/view/src/main/res/values-hr/strings.xml
index 7ecedf6..5ec52b8 100644
--- a/slices/view/src/main/res/values-hr/strings.xml
+++ b/slices/view/src/main/res/values-hr/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"još <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Više"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-hu/strings.xml b/slices/view/src/main/res/values-hu/strings.xml
index ea5ab25..957dd68 100644
--- a/slices/view/src/main/res/values-hu/strings.xml
+++ b/slices/view/src/main/res/values-hu/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Több"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Több megjelenítése"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-hy/strings.xml b/slices/view/src/main/res/values-hy/strings.xml
index ea5ab25..49e731b 100644
--- a/slices/view/src/main/res/values-hy/strings.xml
+++ b/slices/view/src/main/res/values-hy/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Ավելին"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Ցուցադրել ավելի շատ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-in/strings.xml b/slices/view/src/main/res/values-in/strings.xml
index ea5ab25..7d46931 100644
--- a/slices/view/src/main/res/values-in/strings.xml
+++ b/slices/view/src/main/res/values-in/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Lainnya"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Tampilkan lainnya"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-is/strings.xml b/slices/view/src/main/res/values-is/strings.xml
index ea5ab25..231dd20 100644
--- a/slices/view/src/main/res/values-is/strings.xml
+++ b/slices/view/src/main/res/values-is/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Meira"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Sýna meira"</string>
</resources>
diff --git a/slices/view/src/main/res/values-it/strings.xml b/slices/view/src/main/res/values-it/strings.xml
index ea5ab25..8f0a761 100644
--- a/slices/view/src/main/res/values-it/strings.xml
+++ b/slices/view/src/main/res/values-it/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Altro"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostra altro"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-iw/strings.xml b/slices/view/src/main/res/values-iw/strings.xml
index ea5ab25..c160611 100644
--- a/slices/view/src/main/res/values-iw/strings.xml
+++ b/slices/view/src/main/res/values-iw/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"עוד"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"הצג יותר"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ja/strings.xml b/slices/view/src/main/res/values-ja/strings.xml
index ed51803..a18751f 100644
--- a/slices/view/src/main/res/values-ja/strings.xml
+++ b/slices/view/src/main/res/values-ja/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"他 <xliff:g id="NUMBER">%1$d</xliff:g> 件"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"もっと見る"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"もっと見る"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ka/strings.xml b/slices/view/src/main/res/values-ka/strings.xml
index ea5ab25..8701f5d 100644
--- a/slices/view/src/main/res/values-ka/strings.xml
+++ b/slices/view/src/main/res/values-ka/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"მეტი"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"მეტის ჩვენება"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-kk/strings.xml b/slices/view/src/main/res/values-kk/strings.xml
index ea5ab25..1867ada 100644
--- a/slices/view/src/main/res/values-kk/strings.xml
+++ b/slices/view/src/main/res/values-kk/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Тағы"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Толығырақ көрсету"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-km/strings.xml b/slices/view/src/main/res/values-km/strings.xml
index ea5ab25..3f50a99 100644
--- a/slices/view/src/main/res/values-km/strings.xml
+++ b/slices/view/src/main/res/values-km/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ច្រើនទៀត"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"បង្ហាញច្រើនទៀត"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-kn/strings.xml b/slices/view/src/main/res/values-kn/strings.xml
index ea5ab25..e8224f3 100644
--- a/slices/view/src/main/res/values-kn/strings.xml
+++ b/slices/view/src/main/res/values-kn/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ಇನ್ನಷ್ಟು"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ಹೆಚ್ಚು ತೋರಿಸಿ"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ko/strings.xml b/slices/view/src/main/res/values-ko/strings.xml
index 2dc0279..8e7cb24 100644
--- a/slices/view/src/main/res/values-ko/strings.xml
+++ b/slices/view/src/main/res/values-ko/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g>개 더보기"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"더보기"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"더보기"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ky/strings.xml b/slices/view/src/main/res/values-ky/strings.xml
index ea5ab25..910ae6d 100644
--- a/slices/view/src/main/res/values-ky/strings.xml
+++ b/slices/view/src/main/res/values-ky/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Дагы"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Дагы көрсөтүү"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-lo/strings.xml b/slices/view/src/main/res/values-lo/strings.xml
index ea5ab25..d266a43 100644
--- a/slices/view/src/main/res/values-lo/strings.xml
+++ b/slices/view/src/main/res/values-lo/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ເພີ່ມເຕີມ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ສະແດງເພີ່ມເຕີມ"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-lt/strings.xml b/slices/view/src/main/res/values-lt/strings.xml
index bb6bcb3..cc4b804 100644
--- a/slices/view/src/main/res/values-lt/strings.xml
+++ b/slices/view/src/main/res/values-lt/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Dar <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Daugiau"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Rodyti daugiau"</string>
</resources>
diff --git a/slices/view/src/main/res/values-lv/strings.xml b/slices/view/src/main/res/values-lv/strings.xml
index 79ccb99..042436d 100644
--- a/slices/view/src/main/res/values-lv/strings.xml
+++ b/slices/view/src/main/res/values-lv/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Vēl <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Vēl"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Rādīt vairāk"</string>
</resources>
diff --git a/slices/view/src/main/res/values-mk/strings.xml b/slices/view/src/main/res/values-mk/strings.xml
index ea5ab25..b3c5da4 100644
--- a/slices/view/src/main/res/values-mk/strings.xml
+++ b/slices/view/src/main/res/values-mk/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Повеќе"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Прикажи повеќе"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ml/strings.xml b/slices/view/src/main/res/values-ml/strings.xml
index ea5ab25..e2dc0b1 100644
--- a/slices/view/src/main/res/values-ml/strings.xml
+++ b/slices/view/src/main/res/values-ml/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"കൂടുതൽ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"കൂടുതൽ കാണിക്കുക"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-mn/strings.xml b/slices/view/src/main/res/values-mn/strings.xml
index ea5ab25..740f455 100644
--- a/slices/view/src/main/res/values-mn/strings.xml
+++ b/slices/view/src/main/res/values-mn/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Бусад"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Дэлгэрэнгүй үзэх"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-mr/strings.xml b/slices/view/src/main/res/values-mr/strings.xml
index ea5ab25..ab06bea 100644
--- a/slices/view/src/main/res/values-mr/strings.xml
+++ b/slices/view/src/main/res/values-mr/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"आणखी"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"आणखी दाखवा"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ms/strings.xml b/slices/view/src/main/res/values-ms/strings.xml
index ea5ab25..03bda10 100644
--- a/slices/view/src/main/res/values-ms/strings.xml
+++ b/slices/view/src/main/res/values-ms/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Lagi"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Tunjukkan lagi"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-my/strings.xml b/slices/view/src/main/res/values-my/strings.xml
index ea5ab25..754d40e 100644
--- a/slices/view/src/main/res/values-my/strings.xml
+++ b/slices/view/src/main/res/values-my/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"နောက်ထပ်"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"နောက်ထပ် ပြပါ"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-nb/strings.xml b/slices/view/src/main/res/values-nb/strings.xml
index ea5ab25..e977965 100644
--- a/slices/view/src/main/res/values-nb/strings.xml
+++ b/slices/view/src/main/res/values-nb/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mer"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Vis mer"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ne/strings.xml b/slices/view/src/main/res/values-ne/strings.xml
index ea5ab25..6a95860 100644
--- a/slices/view/src/main/res/values-ne/strings.xml
+++ b/slices/view/src/main/res/values-ne/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"थप"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"थप देखाउनुहोस्"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-nl/strings.xml b/slices/view/src/main/res/values-nl/strings.xml
index ea5ab25..ed28ef2 100644
--- a/slices/view/src/main/res/values-nl/strings.xml
+++ b/slices/view/src/main/res/values-nl/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Meer"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Meer weergeven"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-pa/strings.xml b/slices/view/src/main/res/values-pa/strings.xml
index ea5ab25..cd6db9b 100644
--- a/slices/view/src/main/res/values-pa/strings.xml
+++ b/slices/view/src/main/res/values-pa/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"ਹੋਰ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ਹੋਰ ਦਿਖਾਓ"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-pl/strings.xml b/slices/view/src/main/res/values-pl/strings.xml
index 9d9bede..5fe2d2b 100644
--- a/slices/view/src/main/res/values-pl/strings.xml
+++ b/slices/view/src/main/res/values-pl/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Więcej"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Pokaż więcej"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-pt-rBR/strings.xml b/slices/view/src/main/res/values-pt-rBR/strings.xml
index 629ba48..a163c4a 100644
--- a/slices/view/src/main/res/values-pt-rBR/strings.xml
+++ b/slices/view/src/main/res/values-pt-rBR/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-pt-rPT/strings.xml b/slices/view/src/main/res/values-pt-rPT/strings.xml
index ea5ab25..7d4c127 100644
--- a/slices/view/src/main/res/values-pt-rPT/strings.xml
+++ b/slices/view/src/main/res/values-pt-rPT/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-pt/strings.xml b/slices/view/src/main/res/values-pt/strings.xml
index 629ba48..a163c4a 100644
--- a/slices/view/src/main/res/values-pt/strings.xml
+++ b/slices/view/src/main/res/values-pt/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ro/strings.xml b/slices/view/src/main/res/values-ro/strings.xml
index ea5ab25..ed61fde 100644
--- a/slices/view/src/main/res/values-ro/strings.xml
+++ b/slices/view/src/main/res/values-ro/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mai mult"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Vedeți mai multe"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ru/strings.xml b/slices/view/src/main/res/values-ru/strings.xml
index 9d9bede..e3e95f7 100644
--- a/slices/view/src/main/res/values-ru/strings.xml
+++ b/slices/view/src/main/res/values-ru/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Ещё"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Ещё"</string>
</resources>
diff --git a/slices/view/src/main/res/values-si/strings.xml b/slices/view/src/main/res/values-si/strings.xml
index ea5ab25..58feb78 100644
--- a/slices/view/src/main/res/values-si/strings.xml
+++ b/slices/view/src/main/res/values-si/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"තව"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"තව පෙන්වන්න"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-sk/strings.xml b/slices/view/src/main/res/values-sk/strings.xml
index ea5ab25..fe77506 100644
--- a/slices/view/src/main/res/values-sk/strings.xml
+++ b/slices/view/src/main/res/values-sk/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Viac"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Zobraziť viac"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-sl/strings.xml b/slices/view/src/main/res/values-sl/strings.xml
index 59bf101..802caf1 100644
--- a/slices/view/src/main/res/values-sl/strings.xml
+++ b/slices/view/src/main/res/values-sl/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"in še <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Več"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Pokaži več"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-sq/strings.xml b/slices/view/src/main/res/values-sq/strings.xml
index ea5ab25..e1cdae8 100644
--- a/slices/view/src/main/res/values-sq/strings.xml
+++ b/slices/view/src/main/res/values-sq/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Më shumë"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Shfaq më shumë"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-sr/strings.xml b/slices/view/src/main/res/values-sr/strings.xml
index a66220a..627ede6 100644
--- a/slices/view/src/main/res/values-sr/strings.xml
+++ b/slices/view/src/main/res/values-sr/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"и још <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Још"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Прикажи више"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sv/strings.xml b/slices/view/src/main/res/values-sv/strings.xml
index cbcec4f..f8ed0c7 100644
--- a/slices/view/src/main/res/values-sv/strings.xml
+++ b/slices/view/src/main/res/values-sv/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> till"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mer"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Visa mer"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-sw/strings.xml b/slices/view/src/main/res/values-sw/strings.xml
index ea5ab25..b23ed5e 100644
--- a/slices/view/src/main/res/values-sw/strings.xml
+++ b/slices/view/src/main/res/values-sw/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Mengine"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Onyesha mengine"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ta/strings.xml b/slices/view/src/main/res/values-ta/strings.xml
index ea5ab25..9f2be0e 100644
--- a/slices/view/src/main/res/values-ta/strings.xml
+++ b/slices/view/src/main/res/values-ta/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"மேலும்"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"மேலும் காட்டு"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-te/strings.xml b/slices/view/src/main/res/values-te/strings.xml
index ea5ab25..0eb1da7 100644
--- a/slices/view/src/main/res/values-te/strings.xml
+++ b/slices/view/src/main/res/values-te/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"మరింత"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"మరింత చూపు"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-th/strings.xml b/slices/view/src/main/res/values-th/strings.xml
index ea5ab25..4cdd991 100644
--- a/slices/view/src/main/res/values-th/strings.xml
+++ b/slices/view/src/main/res/values-th/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"เพิ่มเติม"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"แสดงเพิ่ม"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-tl/strings.xml b/slices/view/src/main/res/values-tl/strings.xml
index ea5ab25..05917b0 100644
--- a/slices/view/src/main/res/values-tl/strings.xml
+++ b/slices/view/src/main/res/values-tl/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Higit pa"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Magpakita pa"</string>
</resources>
diff --git a/slices/view/src/main/res/values-tr/strings.xml b/slices/view/src/main/res/values-tr/strings.xml
index ea5ab25..09a589e 100644
--- a/slices/view/src/main/res/values-tr/strings.xml
+++ b/slices/view/src/main/res/values-tr/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Diğer"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Daha fazla göster"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-uk/strings.xml b/slices/view/src/main/res/values-uk/strings.xml
index ea5ab25..06c02bc 100644
--- a/slices/view/src/main/res/values-uk/strings.xml
+++ b/slices/view/src/main/res/values-uk/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Більше"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Показати більше"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-ur/strings.xml b/slices/view/src/main/res/values-ur/strings.xml
index ce427c0..f12d6b3 100644
--- a/slices/view/src/main/res/values-ur/strings.xml
+++ b/slices/view/src/main/res/values-ur/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"مزید"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"مزید دکھائیں"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-uz/strings.xml b/slices/view/src/main/res/values-uz/strings.xml
index ea5ab25..cbc9979 100644
--- a/slices/view/src/main/res/values-uz/strings.xml
+++ b/slices/view/src/main/res/values-uz/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Yana"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Yana"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-vi/strings.xml b/slices/view/src/main/res/values-vi/strings.xml
index ea5ab25..3890636 100644
--- a/slices/view/src/main/res/values-vi/strings.xml
+++ b/slices/view/src/main/res/values-vi/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Thêm"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Hiển thị thêm"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-zh-rCN/strings.xml b/slices/view/src/main/res/values-zh-rCN/strings.xml
index ea5ab25..ffb63a4 100644
--- a/slices/view/src/main/res/values-zh-rCN/strings.xml
+++ b/slices/view/src/main/res/values-zh-rCN/strings.xml
@@ -18,4 +18,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"显示更多"</string>
</resources>
diff --git a/slices/view/src/main/res/values-zh-rHK/strings.xml b/slices/view/src/main/res/values-zh-rHK/strings.xml
index ea5ab25..565b565 100644
--- a/slices/view/src/main/res/values-zh-rHK/strings.xml
+++ b/slices/view/src/main/res/values-zh-rHK/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"顯示更多"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-zh-rTW/strings.xml b/slices/view/src/main/res/values-zh-rTW/strings.xml
index ea5ab25..565b565 100644
--- a/slices/view/src/main/res/values-zh-rTW/strings.xml
+++ b/slices/view/src/main/res/values-zh-rTW/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"顯示更多"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/slices/view/src/main/res/values-zu/strings.xml b/slices/view/src/main/res/values-zu/strings.xml
index ea5ab25..c5ebb10 100644
--- a/slices/view/src/main/res/values-zu/strings.xml
+++ b/slices/view/src/main/res/values-zu/strings.xml
@@ -18,4 +18,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="abc_slice_more" msgid="1983560225998630901">"Okuningi"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"Bonisa okuningi"</string>
+ <!-- no translation found for abc_slice_updated (8155085405396453848) -->
+ <skip />
+ <!-- no translation found for abc_slice_error (4188371422904147368) -->
+ <skip />
</resources>
diff --git a/testutils-ktx/NO_DOCS b/testutils-ktx/NO_DOCS
new file mode 100644
index 0000000..4dad694
--- /dev/null
+++ b/testutils-ktx/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2017 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/testutils-ktx/OWNERS b/testutils-ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/testutils-ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/testutils-ktx/build.gradle b/testutils-ktx/build.gradle
new file mode 100644
index 0000000..5f33da1
--- /dev/null
+++ b/testutils-ktx/build.gradle
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+ id("SupportKotlinLibraryPlugin")
+}
+
+dependencies {
+ compile(KOTLIN_STDLIB)
+ compile(TRUTH)
+}
diff --git a/testutils-ktx/src/main/java/androidx/testutils/assertions.kt b/testutils-ktx/src/main/java/androidx/testutils/assertions.kt
new file mode 100644
index 0000000..b67948a
--- /dev/null
+++ b/testutils-ktx/src/main/java/androidx/testutils/assertions.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 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 androidx.testutils
+
+import com.google.common.truth.ThrowableSubject
+import com.google.common.truth.Truth.assertThat
+
+inline fun <reified T : Throwable> assertThrows(body: () -> Unit): ThrowableSubject {
+ try {
+ body()
+ } catch (e: Throwable) {
+ if (e is T) {
+ return assertThat(e)
+ }
+ throw e
+ }
+ throw AssertionError("Body completed successfully. Expected ${T::class.java.simpleName}.")
+}
+
+fun fail(message: String? = null): Nothing = throw AssertionError(message)
diff --git a/v7/appcompat/res/values-af/strings.xml b/v7/appcompat/res/values-af/strings.xml
index 15ed3b2..b7dd9bc 100644
--- a/v7/appcompat/res/values-af/strings.xml
+++ b/v7/appcompat/res/values-af/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AAN"</string>
<string name="abc_capital_off" msgid="121134116657445385">"AF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Soek"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Kieslys+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Simbool+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funksie+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spasiebalk"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-am/strings.xml b/v7/appcompat/res/values-am/strings.xml
index 42bbc06..485ffad 100644
--- a/v7/appcompat/res/values-am/strings.xml
+++ b/v7/appcompat/res/values-am/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"በርቷል"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ጠፍቷል"</string>
<string name="search_menu_title" msgid="146198913615257606">"ፈልግ"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"ምናሌ+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"ሰርዝ"</string>
</resources>
diff --git a/v7/appcompat/res/values-ar/strings.xml b/v7/appcompat/res/values-ar/strings.xml
index 3278162..a7683ce 100644
--- a/v7/appcompat/res/values-ar/strings.xml
+++ b/v7/appcompat/res/values-ar/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"تشغيل"</string>
<string name="abc_capital_off" msgid="121134116657445385">"إيقاف"</string>
<string name="search_menu_title" msgid="146198913615257606">"البحث"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"القائمة+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-az/strings.xml b/v7/appcompat/res/values-az/strings.xml
index 29e00dd..aae3b94 100644
--- a/v7/appcompat/res/values-az/strings.xml
+++ b/v7/appcompat/res/values-az/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AKTİV"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DEAKTİV"</string>
<string name="search_menu_title" msgid="146198913615257606">"Axtarış"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menyu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funksiya+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"kosmos"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"daxil olun"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"silin"</string>
</resources>
diff --git a/v7/appcompat/res/values-b+sr+Latn/strings.xml b/v7/appcompat/res/values-b+sr+Latn/strings.xml
index c3462f6..734525b 100644
--- a/v7/appcompat/res/values-b+sr+Latn/strings.xml
+++ b/v7/appcompat/res/values-b+sr+Latn/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČI"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČI"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pretraži"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"taster za razmak"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-be/strings.xml b/v7/appcompat/res/values-be/strings.xml
index 99ee19f..f04076a 100644
--- a/v7/appcompat/res/values-be/strings.xml
+++ b/v7/appcompat/res/values-be/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"УКЛ."</string>
<string name="abc_capital_off" msgid="121134116657445385">"ВЫКЛ."</string>
<string name="search_menu_title" msgid="146198913615257606">"Пошук"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Меню +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Прабел"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-bg/strings.xml b/v7/appcompat/res/values-bg/strings.xml
index 1d37d0d..2c607a1 100644
--- a/v7/appcompat/res/values-bg/strings.xml
+++ b/v7/appcompat/res/values-bg/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ВКЛ."</string>
<string name="abc_capital_off" msgid="121134116657445385">"ИЗКЛ."</string>
<string name="search_menu_title" msgid="146198913615257606">"Търсене"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"клавиша за интервал"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-bn/strings.xml b/v7/appcompat/res/values-bn/strings.xml
index 2ea7591..a6be993 100644
--- a/v7/appcompat/res/values-bn/strings.xml
+++ b/v7/appcompat/res/values-bn/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"চালু"</string>
<string name="abc_capital_off" msgid="121134116657445385">"বন্ধ"</string>
<string name="search_menu_title" msgid="146198913615257606">"খুঁজুন"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"মেনু+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"স্পেস"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"মুছুন"</string>
</resources>
diff --git a/v7/appcompat/res/values-bs/strings.xml b/v7/appcompat/res/values-bs/strings.xml
index 07d1411..91281fc 100644
--- a/v7/appcompat/res/values-bs/strings.xml
+++ b/v7/appcompat/res/values-bs/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČI"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČI"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pretraži"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"razmaknica"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ca/strings.xml b/v7/appcompat/res/values-ca/strings.xml
index 03ebec3..c4f3b4d 100644
--- a/v7/appcompat/res/values-ca/strings.xml
+++ b/v7/appcompat/res/values-ca/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAT"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESACTIVAT"</string>
<string name="search_menu_title" msgid="146198913615257606">"Cerca"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menú+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Maj+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funció+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Espai"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Retorn"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Supr"</string>
</resources>
diff --git a/v7/appcompat/res/values-cs/strings.xml b/v7/appcompat/res/values-cs/strings.xml
index 05cd4e0..9111883 100644
--- a/v7/appcompat/res/values-cs/strings.xml
+++ b/v7/appcompat/res/values-cs/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ZAPNUTO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"VYPNUTO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Hledat"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"mezerník"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-da/strings.xml b/v7/appcompat/res/values-da/strings.xml
index 813885a..f05e142 100644
--- a/v7/appcompat/res/values-da/strings.xml
+++ b/v7/appcompat/res/values-da/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"TIL"</string>
<string name="abc_capital_off" msgid="121134116657445385">"FRA"</string>
<string name="search_menu_title" msgid="146198913615257606">"Søg"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"mellemrum"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-de/strings.xml b/v7/appcompat/res/values-de/strings.xml
index 0b57259..fb0dd6a 100644
--- a/v7/appcompat/res/values-de/strings.xml
+++ b/v7/appcompat/res/values-de/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"An"</string>
<string name="abc_capital_off" msgid="121134116657445385">"Aus"</string>
<string name="search_menu_title" msgid="146198913615257606">"Suchen"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menütaste +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta-Taste +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Strg +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Umschalttaste +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym-Taste +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funktionstaste +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Leertaste +"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Eingabetaste"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Löschen"</string>
</resources>
diff --git a/v7/appcompat/res/values-el/strings.xml b/v7/appcompat/res/values-el/strings.xml
index ec7a666..b7da605 100644
--- a/v7/appcompat/res/values-el/strings.xml
+++ b/v7/appcompat/res/values-el/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ΕΝΕΡΓΟΠΟΙΗΣΗ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ΑΠΕΝΕΡΓΟΠΟΙΗΣΗ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Αναζήτηση"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"διάστημα"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-en-rAU/strings.xml b/v7/appcompat/res/values-en-rAU/strings.xml
index a4d048c..a315670 100644
--- a/v7/appcompat/res/values-en-rAU/strings.xml
+++ b/v7/appcompat/res/values-en-rAU/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Search"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-en-rCA/strings.xml b/v7/appcompat/res/values-en-rCA/strings.xml
index a4d048c..a315670 100644
--- a/v7/appcompat/res/values-en-rCA/strings.xml
+++ b/v7/appcompat/res/values-en-rCA/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Search"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-en-rGB/strings.xml b/v7/appcompat/res/values-en-rGB/strings.xml
index a4d048c..a315670 100644
--- a/v7/appcompat/res/values-en-rGB/strings.xml
+++ b/v7/appcompat/res/values-en-rGB/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Search"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-en-rIN/strings.xml b/v7/appcompat/res/values-en-rIN/strings.xml
index a4d048c..a315670 100644
--- a/v7/appcompat/res/values-en-rIN/strings.xml
+++ b/v7/appcompat/res/values-en-rIN/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Search"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-en-rXC/strings.xml b/v7/appcompat/res/values-en-rXC/strings.xml
index b1d5f93..2e8e581 100644
--- a/v7/appcompat/res/values-en-rXC/strings.xml
+++ b/v7/appcompat/res/values-en-rXC/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Search"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-es-rUS/strings.xml b/v7/appcompat/res/values-es-rUS/strings.xml
index 0cc8a70..5d58df0 100644
--- a/v7/appcompat/res/values-es-rUS/strings.xml
+++ b/v7/appcompat/res/values-es-rUS/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVADO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESACTIVADO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menú+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Mayúscula+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Función+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espacio"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"intro"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"borrar"</string>
</resources>
diff --git a/v7/appcompat/res/values-es/strings.xml b/v7/appcompat/res/values-es/strings.xml
index 3e0828b..5238229 100644
--- a/v7/appcompat/res/values-es/strings.xml
+++ b/v7/appcompat/res/values-es/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVADO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESACTIVADO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menú +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Mayús +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Función +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Espacio"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Intro"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Eliminar"</string>
</resources>
diff --git a/v7/appcompat/res/values-et/strings.xml b/v7/appcompat/res/values-et/strings.xml
index 3a3dcbf..b944f77 100644
--- a/v7/appcompat/res/values-et/strings.xml
+++ b/v7/appcompat/res/values-et/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"SEES"</string>
<string name="abc_capital_off" msgid="121134116657445385">"VÄLJAS"</string>
<string name="search_menu_title" msgid="146198913615257606">"Otsing"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menüü +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Tõstuklahv +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funktsiooniklahv +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"tühik"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"sisestusklahv"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"kustutamisklahv"</string>
</resources>
diff --git a/v7/appcompat/res/values-eu/strings.xml b/v7/appcompat/res/values-eu/strings.xml
index a651036..5a45e61 100644
--- a/v7/appcompat/res/values-eu/strings.xml
+++ b/v7/appcompat/res/values-eu/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AKTIBATUTA"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESAKTIBATUTA"</string>
<string name="search_menu_title" msgid="146198913615257606">"Bilatu"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menua +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ktrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Maius +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funtzioa +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Zuriunea"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Sartu"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Ezabatu"</string>
</resources>
diff --git a/v7/appcompat/res/values-fa/strings.xml b/v7/appcompat/res/values-fa/strings.xml
index 9b844b0..35e6596 100644
--- a/v7/appcompat/res/values-fa/strings.xml
+++ b/v7/appcompat/res/values-fa/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"روشن"</string>
<string name="abc_capital_off" msgid="121134116657445385">"خاموش"</string>
<string name="search_menu_title" msgid="146198913615257606">"جستجو"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"منو+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"کلید فاصله"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-fi/strings.xml b/v7/appcompat/res/values-fi/strings.xml
index e9bd952..829b6bb 100644
--- a/v7/appcompat/res/values-fi/strings.xml
+++ b/v7/appcompat/res/values-fi/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"KÄYTÖSSÄ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"POIS KÄYTÖSTÄ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Haku"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Valikko+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Vaihto+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"välilyönti"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-fr-rCA/strings.xml b/v7/appcompat/res/values-fr-rCA/strings.xml
index a3e763b..9e83462 100644
--- a/v7/appcompat/res/values-fr-rCA/strings.xml
+++ b/v7/appcompat/res/values-fr-rCA/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVÉ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DÉSACTIVÉ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Rechercher"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Méta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Maj+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fonction+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espace"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"entrée"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"supprimer"</string>
</resources>
diff --git a/v7/appcompat/res/values-fr/strings.xml b/v7/appcompat/res/values-fr/strings.xml
index 1e412ec..5df86b5 100644
--- a/v7/appcompat/res/values-fr/strings.xml
+++ b/v7/appcompat/res/values-fr/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVÉ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DÉSACTIVÉ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Rechercher"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Méta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Maj+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fonction+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espace"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"entrée"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"supprimer"</string>
</resources>
diff --git a/v7/appcompat/res/values-gl/strings.xml b/v7/appcompat/res/values-gl/strings.xml
index 2af80a1..c500af5 100644
--- a/v7/appcompat/res/values-gl/strings.xml
+++ b/v7/appcompat/res/values-gl/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAR"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESACTIVAR"</string>
<string name="search_menu_title" msgid="146198913615257606">"Buscar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menú +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Maiús +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sim +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Función +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espazo"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Intro"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"eliminar"</string>
</resources>
diff --git a/v7/appcompat/res/values-gu/strings.xml b/v7/appcompat/res/values-gu/strings.xml
index 7a243ed..a8b0c48 100644
--- a/v7/appcompat/res/values-gu/strings.xml
+++ b/v7/appcompat/res/values-gu/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ચાલુ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"બંધ"</string>
<string name="search_menu_title" msgid="146198913615257606">"શોધો"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"મેનૂ+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Spacebar"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"ડિલીટ કરો"</string>
</resources>
diff --git a/v7/appcompat/res/values-hi/strings.xml b/v7/appcompat/res/values-hi/strings.xml
index a31ab90..16dc7ce 100644
--- a/v7/appcompat/res/values-hi/strings.xml
+++ b/v7/appcompat/res/values-hi/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"चालू"</string>
<string name="abc_capital_off" msgid="121134116657445385">"बंद"</string>
<string name="search_menu_title" msgid="146198913615257606">"सर्च"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-hr/strings.xml b/v7/appcompat/res/values-hr/strings.xml
index 27a1c2e..9253fb9 100644
--- a/v7/appcompat/res/values-hr/strings.xml
+++ b/v7/appcompat/res/values-hr/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČENO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČENO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pretraživanje"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"razmaknica"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-hu/strings.xml b/v7/appcompat/res/values-hu/strings.xml
index d3e413f..714ef47 100644
--- a/v7/appcompat/res/values-hu/strings.xml
+++ b/v7/appcompat/res/values-hu/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"BE"</string>
<string name="abc_capital_off" msgid="121134116657445385">"KI"</string>
<string name="search_menu_title" msgid="146198913615257606">"Keresés"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Szóköz"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-hy/strings.xml b/v7/appcompat/res/values-hy/strings.xml
index 1c41ef6..59c9ee5 100644
--- a/v7/appcompat/res/values-hy/strings.xml
+++ b/v7/appcompat/res/values-hy/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ՄԻԱՑՎԱԾ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ԱՆՋԱՏՎԱԾ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Որոնել"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"բացատ"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-in/strings.xml b/v7/appcompat/res/values-in/strings.xml
index 2e9fbb7..1ae3f90 100644
--- a/v7/appcompat/res/values-in/strings.xml
+++ b/v7/appcompat/res/values-in/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AKTIF"</string>
<string name="abc_capital_off" msgid="121134116657445385">"NONAKTIF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Telusuri"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spasi"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-is/strings.xml b/v7/appcompat/res/values-is/strings.xml
index 3f61d84..3ae59da 100644
--- a/v7/appcompat/res/values-is/strings.xml
+++ b/v7/appcompat/res/values-is/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"KVEIKT"</string>
<string name="abc_capital_off" msgid="121134116657445385">"SLÖKKT"</string>
<string name="search_menu_title" msgid="146198913615257606">"Leita"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Valmynd+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Aðgerðarlykill+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"bilslá"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-it/strings.xml b/v7/appcompat/res/values-it/strings.xml
index fbd2c58..04b64ce 100644
--- a/v7/appcompat/res/values-it/strings.xml
+++ b/v7/appcompat/res/values-it/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Ricerca"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"MENU +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"META +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"CTRL +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"ALT +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"MAIUSC +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"SYM +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"FUNZIONE +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"barra spaziatrice"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"INVIO"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"CANC"</string>
</resources>
diff --git a/v7/appcompat/res/values-iw/strings.xml b/v7/appcompat/res/values-iw/strings.xml
index 0a7d0bb..d0f2211 100644
--- a/v7/appcompat/res/values-iw/strings.xml
+++ b/v7/appcompat/res/values-iw/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"פועל"</string>
<string name="abc_capital_off" msgid="121134116657445385">"כבוי"</string>
<string name="search_menu_title" msgid="146198913615257606">"חיפוש"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"תפריט+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"מקש רווח"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ja/strings.xml b/v7/appcompat/res/values-ja/strings.xml
index c4d0e20..db163f3 100644
--- a/v7/appcompat/res/values-ja/strings.xml
+++ b/v7/appcompat/res/values-ja/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"検索"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ka/strings.xml b/v7/appcompat/res/values-ka/strings.xml
index 3b077a3..6d9c08b 100644
--- a/v7/appcompat/res/values-ka/strings.xml
+++ b/v7/appcompat/res/values-ka/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ჩართულია"</string>
<string name="abc_capital_off" msgid="121134116657445385">"გამორთულია"</string>
<string name="search_menu_title" msgid="146198913615257606">"ძიება"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"შეყვანა"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"წაშლა"</string>
</resources>
diff --git a/v7/appcompat/res/values-kk/strings.xml b/v7/appcompat/res/values-kk/strings.xml
index c32045b..9d206bc 100644
--- a/v7/appcompat/res/values-kk/strings.xml
+++ b/v7/appcompat/res/values-kk/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ҚОСУЛЫ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ӨШІРУЛІ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Іздеу"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Mәзір+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"бос орын"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-km/strings.xml b/v7/appcompat/res/values-km/strings.xml
index ffe289a..f42da7a 100644
--- a/v7/appcompat/res/values-km/strings.xml
+++ b/v7/appcompat/res/values-km/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"បើក"</string>
<string name="abc_capital_off" msgid="121134116657445385">"បិទ"</string>
<string name="search_menu_title" msgid="146198913615257606">"ស្វែងរក"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-kn/strings.xml b/v7/appcompat/res/values-kn/strings.xml
index 4218bd6..ce28303 100644
--- a/v7/appcompat/res/values-kn/strings.xml
+++ b/v7/appcompat/res/values-kn/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ಆನ್"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ಆಫ್"</string>
<string name="search_menu_title" msgid="146198913615257606">"ಹುಡುಕಿ"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ko/strings.xml b/v7/appcompat/res/values-ko/strings.xml
index 6c84a2a..db0903f 100644
--- a/v7/appcompat/res/values-ko/strings.xml
+++ b/v7/appcompat/res/values-ko/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"사용"</string>
<string name="abc_capital_off" msgid="121134116657445385">"사용 안함"</string>
<string name="search_menu_title" msgid="146198913615257606">"검색"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"스페이스바"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"입력"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"삭제"</string>
</resources>
diff --git a/v7/appcompat/res/values-ky/strings.xml b/v7/appcompat/res/values-ky/strings.xml
index 66202f7..1aba4f0 100644
--- a/v7/appcompat/res/values-ky/strings.xml
+++ b/v7/appcompat/res/values-ky/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"КҮЙҮК"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ӨЧҮК"</string>
<string name="search_menu_title" msgid="146198913615257606">"Издөө"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"боштук"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-lo/strings.xml b/v7/appcompat/res/values-lo/strings.xml
index 1b92df0..c53a987 100644
--- a/v7/appcompat/res/values-lo/strings.xml
+++ b/v7/appcompat/res/values-lo/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ເປີດ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ປິດ"</string>
<string name="search_menu_title" msgid="146198913615257606">"ຊອກຫາ"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-lt/strings.xml b/v7/appcompat/res/values-lt/strings.xml
index 5793069..ab9f25c 100644
--- a/v7/appcompat/res/values-lt/strings.xml
+++ b/v7/appcompat/res/values-lt/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ĮJUNGTI"</string>
<string name="abc_capital_off" msgid="121134116657445385">"IŠJUNGTA"</string>
<string name="search_menu_title" msgid="146198913615257606">"Paieška"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"„Menu“ +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"„Meta“ +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"„Ctrl“ +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"„Alt“ +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"„Shift“ +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"„Sym“ +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"„Function“ +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"tarpo klavišas"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"„Enter“"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"„Delete“"</string>
</resources>
diff --git a/v7/appcompat/res/values-lv/strings.xml b/v7/appcompat/res/values-lv/strings.xml
index 67e18d3..67904a1 100644
--- a/v7/appcompat/res/values-lv/strings.xml
+++ b/v7/appcompat/res/values-lv/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"IESLĒGTS"</string>
<string name="abc_capital_off" msgid="121134116657445385">"IZSLĒGTS"</string>
<string name="search_menu_title" msgid="146198913615257606">"Meklēt"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Poga Izvēlne +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta taustiņš +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Vadīšanas taustiņš +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alternēšanas taustiņš +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Pārslēgšanas taustiņš +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Simbolu taustiņš +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funkcijas taustiņš +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"atstarpes taustiņš"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"ievadīšanas taustiņš"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"dzēšanas taustiņš"</string>
</resources>
diff --git a/v7/appcompat/res/values-mk/strings.xml b/v7/appcompat/res/values-mk/strings.xml
index b12a235..408edb1 100644
--- a/v7/appcompat/res/values-mk/strings.xml
+++ b/v7/appcompat/res/values-mk/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ВКЛУЧЕНО"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ИСКЛУЧЕНО"</string>
<string name="search_menu_title" msgid="146198913615257606">"Пребарај"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Мени+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"копче Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"копче Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"копче Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"копче Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"копче Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"копче Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"вселена"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"копче enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"избриши"</string>
</resources>
diff --git a/v7/appcompat/res/values-ml/strings.xml b/v7/appcompat/res/values-ml/strings.xml
index 9033f8a..e880ebb 100644
--- a/v7/appcompat/res/values-ml/strings.xml
+++ b/v7/appcompat/res/values-ml/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ഓൺ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ഓഫ്"</string>
<string name="search_menu_title" msgid="146198913615257606">"തിരയുക"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"മെനു+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"മെറ്റ+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"ഫംഗ്ഷന്+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"സ്പെയ്സ്"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"ഇല്ലാതാക്കുക"</string>
</resources>
diff --git a/v7/appcompat/res/values-mn/strings.xml b/v7/appcompat/res/values-mn/strings.xml
index 56036ea..856e9f6 100644
--- a/v7/appcompat/res/values-mn/strings.xml
+++ b/v7/appcompat/res/values-mn/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ИДЭВХТЭЙ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ИДЭВХГҮЙ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Хайлт"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Цэс+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Мета+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Функц+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"зай"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"оруулах"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"устгах"</string>
</resources>
diff --git a/v7/appcompat/res/values-mr/strings.xml b/v7/appcompat/res/values-mr/strings.xml
index 26ada80..ed3463c 100644
--- a/v7/appcompat/res/values-mr/strings.xml
+++ b/v7/appcompat/res/values-mr/strings.xml
@@ -30,8 +30,18 @@
<string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"एक अॅप निवडा"</string>
<string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"सर्व पहा"</string>
<string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> सह शेअर करा"</string>
- <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"यांच्यासह सामायिक करा"</string>
+ <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"यांच्यासह शेअर करा"</string>
<string name="abc_capital_on" msgid="3405795526292276155">"चालू"</string>
<string name="abc_capital_off" msgid="121134116657445385">"बंद"</string>
<string name="search_menu_title" msgid="146198913615257606">"शोधा"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"मेनू+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spacebar"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"एंटर करा"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"हटवा"</string>
</resources>
diff --git a/v7/appcompat/res/values-ms/strings.xml b/v7/appcompat/res/values-ms/strings.xml
index 18f84ce..8af4c13 100644
--- a/v7/appcompat/res/values-ms/strings.xml
+++ b/v7/appcompat/res/values-ms/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"HIDUP"</string>
<string name="abc_capital_off" msgid="121134116657445385">"MATI"</string>
<string name="search_menu_title" msgid="146198913615257606">"Cari"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fungsi+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"ruang"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"padam"</string>
</resources>
diff --git a/v7/appcompat/res/values-my/strings.xml b/v7/appcompat/res/values-my/strings.xml
index cbc8791..cfd6625 100644
--- a/v7/appcompat/res/values-my/strings.xml
+++ b/v7/appcompat/res/values-my/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ဖွင့်"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ပိတ်"</string>
<string name="search_menu_title" msgid="146198913615257606">"ရှာဖွေပါ"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-nb/strings.xml b/v7/appcompat/res/values-nb/strings.xml
index 6005234..f9fd80e 100644
--- a/v7/appcompat/res/values-nb/strings.xml
+++ b/v7/appcompat/res/values-nb/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"PÅ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"AV"</string>
<string name="search_menu_title" msgid="146198913615257606">"Søk"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Meny+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funksjon+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"mellomrom"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ne/strings.xml b/v7/appcompat/res/values-ne/strings.xml
index 96b1042..0a0de14 100644
--- a/v7/appcompat/res/values-ne/strings.xml
+++ b/v7/appcompat/res/values-ne/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"सक्रिय गर्नुहोस्"</string>
<string name="abc_capital_off" msgid="121134116657445385">"निष्क्रिय पार्नुहोस्"</string>
<string name="search_menu_title" msgid="146198913615257606">"खोज्नुहोस्"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-nl/strings.xml b/v7/appcompat/res/values-nl/strings.xml
index e0d2044..ffcb3d9 100644
--- a/v7/appcompat/res/values-nl/strings.xml
+++ b/v7/appcompat/res/values-nl/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AAN"</string>
<string name="abc_capital_off" msgid="121134116657445385">"UIT"</string>
<string name="search_menu_title" msgid="146198913615257606">"Zoeken"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Functie +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spatie"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"verwijderen"</string>
</resources>
diff --git a/v7/appcompat/res/values-or/strings.xml b/v7/appcompat/res/values-or/strings.xml
index 4dc6eb4..4486724 100644
--- a/v7/appcompat/res/values-or/strings.xml
+++ b/v7/appcompat/res/values-or/strings.xml
@@ -32,10 +32,27 @@
<!-- no translation found for abc_shareactionprovider_share_with_application (3300176832234831527) -->
<skip />
<string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ଏହାଙ୍କ ସହ ଶେୟାର୍ କରନ୍ତୁ"</string>
- <!-- no translation found for abc_capital_on (3405795526292276155) -->
+ <string name="abc_capital_on" msgid="3405795526292276155">"ଅନ୍"</string>
+ <string name="abc_capital_off" msgid="121134116657445385">"ଅଫ୍"</string>
+ <string name="search_menu_title" msgid="146198913615257606">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
+ <!-- no translation found for abc_prepend_shortcut_label (1351762916121158029) -->
<skip />
- <!-- no translation found for abc_capital_off (121134116657445385) -->
+ <!-- no translation found for abc_menu_meta_shortcut_label (7643535737296831317) -->
<skip />
- <!-- no translation found for search_menu_title (146198913615257606) -->
+ <!-- no translation found for abc_menu_ctrl_shortcut_label (1324831542140195728) -->
+ <skip />
+ <!-- no translation found for abc_menu_alt_shortcut_label (1302280443949172191) -->
+ <skip />
+ <!-- no translation found for abc_menu_shift_shortcut_label (8126296154200614004) -->
+ <skip />
+ <!-- no translation found for abc_menu_sym_shortcut_label (9002602288060866689) -->
+ <skip />
+ <!-- no translation found for abc_menu_function_shortcut_label (4792426091847145555) -->
+ <skip />
+ <!-- no translation found for abc_menu_space_shortcut_label (2378550843553983978) -->
+ <skip />
+ <!-- no translation found for abc_menu_enter_shortcut_label (8341180395196749340) -->
+ <skip />
+ <!-- no translation found for abc_menu_delete_shortcut_label (8362206064229013510) -->
<skip />
</resources>
diff --git a/v7/appcompat/res/values-pa/strings.xml b/v7/appcompat/res/values-pa/strings.xml
index 7f28ac8..fc1fdba 100644
--- a/v7/appcompat/res/values-pa/strings.xml
+++ b/v7/appcompat/res/values-pa/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ਤੇ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ਬੰਦ"</string>
<string name="search_menu_title" msgid="146198913615257606">"ਖੋਜੋ"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-pl/strings.xml b/v7/appcompat/res/values-pl/strings.xml
index d706241..af26367 100644
--- a/v7/appcompat/res/values-pl/strings.xml
+++ b/v7/appcompat/res/values-pl/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"WŁ."</string>
<string name="abc_capital_off" msgid="121134116657445385">"WYŁ."</string>
<string name="search_menu_title" msgid="146198913615257606">"Szukaj"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funkcyjny+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spacja"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-pt-rBR/strings.xml b/v7/appcompat/res/values-pt-rBR/strings.xml
index 90461ec..17d8593 100644
--- a/v7/appcompat/res/values-pt-rBR/strings.xml
+++ b/v7/appcompat/res/values-pt-rBR/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ATIVAR"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESATIVAR"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pesquisar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espaço"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-pt-rPT/strings.xml b/v7/appcompat/res/values-pt-rPT/strings.xml
index 40f6499..f8c0fa7 100644
--- a/v7/appcompat/res/values-pt-rPT/strings.xml
+++ b/v7/appcompat/res/values-pt-rPT/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ATIVADO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESATIVADO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pesquisar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Função +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espaço"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"eliminar"</string>
</resources>
diff --git a/v7/appcompat/res/values-pt/strings.xml b/v7/appcompat/res/values-pt/strings.xml
index 90461ec..17d8593 100644
--- a/v7/appcompat/res/values-pt/strings.xml
+++ b/v7/appcompat/res/values-pt/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ATIVAR"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DESATIVAR"</string>
<string name="search_menu_title" msgid="146198913615257606">"Pesquisar"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"espaço"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ro/strings.xml b/v7/appcompat/res/values-ro/strings.xml
index 6d04be9..86495fd 100644
--- a/v7/appcompat/res/values-ro/strings.xml
+++ b/v7/appcompat/res/values-ro/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ACTIVAT"</string>
<string name="abc_capital_off" msgid="121134116657445385">"DEZACTIVAȚI"</string>
<string name="search_menu_title" msgid="146198913615257606">"Căutați"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Meniu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funcție+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"spațiu"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ru/strings.xml b/v7/appcompat/res/values-ru/strings.xml
index 2b28958..c505bf5 100644
--- a/v7/appcompat/res/values-ru/strings.xml
+++ b/v7/appcompat/res/values-ru/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ВКЛ."</string>
<string name="abc_capital_off" msgid="121134116657445385">"ОТКЛ."</string>
<string name="search_menu_title" msgid="146198913615257606">"Поиск"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Меню +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Пробел"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Ввод"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-si/strings.xml b/v7/appcompat/res/values-si/strings.xml
index 7631288..c154686 100644
--- a/v7/appcompat/res/values-si/strings.xml
+++ b/v7/appcompat/res/values-si/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ක්රියාත්මකයි"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ක්රියාවිරහිතයි"</string>
<string name="search_menu_title" msgid="146198913615257606">"සොයන්න"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"මකන්න"</string>
</resources>
diff --git a/v7/appcompat/res/values-sk/strings.xml b/v7/appcompat/res/values-sk/strings.xml
index 03faf14..67184b0 100644
--- a/v7/appcompat/res/values-sk/strings.xml
+++ b/v7/appcompat/res/values-sk/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ZAPNUTÉ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"VYPNUTÉ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Vyhľadávanie"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"medzerník"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"odstrániť"</string>
</resources>
diff --git a/v7/appcompat/res/values-sl/strings.xml b/v7/appcompat/res/values-sl/strings.xml
index 22b8bd4..e38e5ea 100644
--- a/v7/appcompat/res/values-sl/strings.xml
+++ b/v7/appcompat/res/values-sl/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"VKLOPLJENO"</string>
<string name="abc_capital_off" msgid="121134116657445385">"IZKLOPLJENO"</string>
<string name="search_menu_title" msgid="146198913615257606">"Iskanje"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Meni +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"preslednica"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-sq/strings.xml b/v7/appcompat/res/values-sq/strings.xml
index 1a1f02e..8b958a2 100644
--- a/v7/appcompat/res/values-sq/strings.xml
+++ b/v7/appcompat/res/values-sq/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AKTIV"</string>
<string name="abc_capital_off" msgid="121134116657445385">"JOAKTIV"</string>
<string name="search_menu_title" msgid="146198913615257606">"Kërko"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menyja+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funksioni+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"hapësirë"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-sr/strings.xml b/v7/appcompat/res/values-sr/strings.xml
index 5678341..4c702bc 100644
--- a/v7/appcompat/res/values-sr/strings.xml
+++ b/v7/appcompat/res/values-sr/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"УКЉУЧИ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ИСКЉУЧИ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Претражи"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"тастер за размак"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-sv/strings.xml b/v7/appcompat/res/values-sv/strings.xml
index 62d470f..72b7f88 100644
--- a/v7/appcompat/res/values-sv/strings.xml
+++ b/v7/appcompat/res/values-sv/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"PÅ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"AV"</string>
<string name="search_menu_title" msgid="146198913615257606">"Sök"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Meny + "</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta + "</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl + "</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt + "</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Skift + "</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Symbol + "</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Funktion + "</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"blanksteg"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"retur"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-sw/strings.xml b/v7/appcompat/res/values-sw/strings.xml
index c575ae0..1c623b7 100644
--- a/v7/appcompat/res/values-sw/strings.xml
+++ b/v7/appcompat/res/values-sw/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"IMEWASHWA"</string>
<string name="abc_capital_off" msgid="121134116657445385">"IMEZIMWA"</string>
<string name="search_menu_title" msgid="146198913615257606">"Tafuta"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menyu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"nafasi"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"futa"</string>
</resources>
diff --git a/v7/appcompat/res/values-ta/strings.xml b/v7/appcompat/res/values-ta/strings.xml
index 971a3db..7aa031a 100644
--- a/v7/appcompat/res/values-ta/strings.xml
+++ b/v7/appcompat/res/values-ta/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ஆன்"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ஆஃப்"</string>
<string name="search_menu_title" msgid="146198913615257606">"தேடு"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"மெனு மற்றும்"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"மெட்டா மற்றும்"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"கண்ட்ரோல் மற்றும்"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"ஆல்ட் மற்றும்"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"ஷிஃப்ட் மற்றும்"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"சிம்பல் மற்றும்"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"ஃபங்ஷன் மற்றும்"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"ஸ்பேஸ்"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"எண்டர்"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"டெலிட்"</string>
</resources>
diff --git a/v7/appcompat/res/values-te/strings.xml b/v7/appcompat/res/values-te/strings.xml
index f7d7577..818f0e3 100644
--- a/v7/appcompat/res/values-te/strings.xml
+++ b/v7/appcompat/res/values-te/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"ఆన్ చేయి"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ఆఫ్ చేయి"</string>
<string name="search_menu_title" msgid="146198913615257606">"వెతుకు"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"స్పేస్"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-th/strings.xml b/v7/appcompat/res/values-th/strings.xml
index f8ea1cd..5ecec86 100644
--- a/v7/appcompat/res/values-th/strings.xml
+++ b/v7/appcompat/res/values-th/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"เปิด"</string>
<string name="abc_capital_off" msgid="121134116657445385">"ปิด"</string>
<string name="search_menu_title" msgid="146198913615257606">"ค้นหา"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"เมนู+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-tl/strings.xml b/v7/appcompat/res/values-tl/strings.xml
index 1ad2689..3ef5df6 100644
--- a/v7/appcompat/res/values-tl/strings.xml
+++ b/v7/appcompat/res/values-tl/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"I-ON"</string>
<string name="abc_capital_off" msgid="121134116657445385">"I-OFF"</string>
<string name="search_menu_title" msgid="146198913615257606">"Maghanap"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-tr/strings.xml b/v7/appcompat/res/values-tr/strings.xml
index fae41d3..153e2c2 100644
--- a/v7/appcompat/res/values-tr/strings.xml
+++ b/v7/appcompat/res/values-tr/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"AÇ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"KAPAT"</string>
<string name="search_menu_title" msgid="146198913615257606">"Ara"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menü+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Üst Karakter+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"İşlev+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"boşluk"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"sil"</string>
</resources>
diff --git a/v7/appcompat/res/values-uk/strings.xml b/v7/appcompat/res/values-uk/strings.xml
index afc74ff..7aed219 100644
--- a/v7/appcompat/res/values-uk/strings.xml
+++ b/v7/appcompat/res/values-uk/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"УВІМК."</string>
<string name="abc_capital_off" msgid="121134116657445385">"ВИМК."</string>
<string name="search_menu_title" msgid="146198913615257606">"Пошук"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"пробіл"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-ur/strings.xml b/v7/appcompat/res/values-ur/strings.xml
index 60ec34a..997d594 100644
--- a/v7/appcompat/res/values-ur/strings.xml
+++ b/v7/appcompat/res/values-ur/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"آن"</string>
<string name="abc_capital_off" msgid="121134116657445385">"آف"</string>
<string name="search_menu_title" msgid="146198913615257606">"تلاش"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-uz/strings.xml b/v7/appcompat/res/values-uz/strings.xml
index 0417cba..f925bd6 100644
--- a/v7/appcompat/res/values-uz/strings.xml
+++ b/v7/appcompat/res/values-uz/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"YONIQ"</string>
<string name="abc_capital_off" msgid="121134116657445385">"O‘CHIQ"</string>
<string name="search_menu_title" msgid="146198913615257606">"Qidirish"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menyu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"Probel"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-vi/strings.xml b/v7/appcompat/res/values-vi/strings.xml
index 4560b4b..e07efed 100644
--- a/v7/appcompat/res/values-vi/strings.xml
+++ b/v7/appcompat/res/values-vi/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"BẬT"</string>
<string name="abc_capital_off" msgid="121134116657445385">"TẮT"</string>
<string name="search_menu_title" msgid="146198913615257606">"Tìm kiếm"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"phím cách"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"delete"</string>
</resources>
diff --git a/v7/appcompat/res/values-zh-rCN/strings.xml b/v7/appcompat/res/values-zh-rCN/strings.xml
index 7b23457..6dc1ea2 100644
--- a/v7/appcompat/res/values-zh-rCN/strings.xml
+++ b/v7/appcompat/res/values-zh-rCN/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"开启"</string>
<string name="abc_capital_off" msgid="121134116657445385">"关闭"</string>
<string name="search_menu_title" msgid="146198913615257606">"搜索"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"空格键"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter 键"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete 键"</string>
</resources>
diff --git a/v7/appcompat/res/values-zh-rHK/strings.xml b/v7/appcompat/res/values-zh-rHK/strings.xml
index fc32117..ce753fd 100644
--- a/v7/appcompat/res/values-zh-rHK/strings.xml
+++ b/v7/appcompat/res/values-zh-rHK/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"開啟"</string>
<string name="abc_capital_off" msgid="121134116657445385">"關閉"</string>
<string name="search_menu_title" msgid="146198913615257606">"搜尋"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"空白鍵"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter 鍵"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"刪除"</string>
</resources>
diff --git a/v7/appcompat/res/values-zh-rTW/strings.xml b/v7/appcompat/res/values-zh-rTW/strings.xml
index 35be873..0f29b8e 100644
--- a/v7/appcompat/res/values-zh-rTW/strings.xml
+++ b/v7/appcompat/res/values-zh-rTW/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"開啟"</string>
<string name="abc_capital_off" msgid="121134116657445385">"關閉"</string>
<string name="search_menu_title" msgid="146198913615257606">"搜尋"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Menu +"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta +"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl +"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt +"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift +"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym +"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Fn +"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"空格鍵"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"Enter 鍵"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"Delete 鍵"</string>
</resources>
diff --git a/v7/appcompat/res/values-zu/strings.xml b/v7/appcompat/res/values-zu/strings.xml
index e84ba7a..e3032f1 100644
--- a/v7/appcompat/res/values-zu/strings.xml
+++ b/v7/appcompat/res/values-zu/strings.xml
@@ -34,4 +34,14 @@
<string name="abc_capital_on" msgid="3405795526292276155">"VULIWE"</string>
<string name="abc_capital_off" msgid="121134116657445385">"VALIWE"</string>
<string name="search_menu_title" msgid="146198913615257606">"Sesha"</string>
+ <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"Imenyu+"</string>
+ <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+ <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+ <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+ <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+ <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+ <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+ <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"space"</string>
+ <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"enter"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"susa"</string>
</resources>
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
new file mode 100644
index 0000000..fb15e2f
--- /dev/null
+++ b/webkit/api/current.txt
@@ -0,0 +1,52 @@
+package androidx.webkit {
+
+ public abstract class ServiceWorkerClientCompat {
+ ctor public ServiceWorkerClientCompat();
+ method public abstract android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
+ }
+
+ public abstract class ServiceWorkerControllerCompat {
+ method public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+ method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+ method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat);
+ }
+
+ public abstract class ServiceWorkerWebSettingsCompat {
+ method public abstract boolean getAllowContentAccess();
+ method public abstract boolean getAllowFileAccess();
+ method public abstract boolean getBlockNetworkLoads();
+ method public abstract int getCacheMode();
+ method public abstract void setAllowContentAccess(boolean);
+ method public abstract void setAllowFileAccess(boolean);
+ method public abstract void setBlockNetworkLoads(boolean);
+ method public abstract void setCacheMode(int);
+ }
+
+ public class WebSettingsCompat {
+ method public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+ method public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+ method public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+ method public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+ method public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+ method public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+ }
+
+ public class WebViewCompat {
+ method public static android.content.pm.PackageInfo getCurrentWebViewPackage(android.content.Context);
+ method public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+ method public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+ method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
+ method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
+ }
+
+ public static abstract interface WebViewCompat.VisualStateCallback {
+ method public abstract void onComplete(long);
+ }
+
+ public class WebViewFeature {
+ method public static boolean isFeatureSupported(java.lang.String);
+ field public static final java.lang.String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+ }
+
+}
+
diff --git a/webkit/build.gradle b/webkit/build.gradle
index 06f3801..d610dad 100644
--- a/webkit/build.gradle
+++ b/webkit/build.gradle
@@ -38,14 +38,17 @@
// Allow compiling the WebView support library boundary interfaces from this project.
main.java.srcDirs += new File(webviewBoundaryInterfacesDir, "src").getAbsolutePath()
}
+
+ buildTypes.all {
+ consumerProguardFiles new File(webviewBoundaryInterfacesDir, "proguard.flags")
+ }
}
supportLibrary {
name = "WebView Support Library"
- publish = false
+ publish = true
mavenVersion = LibraryVersions.SUPPORT_LIBRARY
mavenGroup = LibraryGroups.WEBKIT
inceptionYear = "2017"
description = "The WebView Support Library is a static library you can add to your Android application in order to use android.webkit APIs that are not available for older platform versions."
- minSdkVersion = 21
}
diff --git a/webkit/src/androidTest/java/androidx/webkit/PollingCheck.java b/webkit/src/androidTest/java/androidx/webkit/PollingCheck.java
new file mode 100644
index 0000000..4c59a3e
--- /dev/null
+++ b/webkit/src/androidTest/java/androidx/webkit/PollingCheck.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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 androidx.webkit;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A class for checking a specific statement {@link #check()} through polling, either until the
+ * statement is true, or until timing out.
+ *
+ * Copy-pasted from CTS: com.android.compatibility.common.util.PollingCheck.
+ */
+public abstract class PollingCheck {
+ private static final long TIME_SLICE = 50;
+ private long mTimeout = 3000;
+
+ public PollingCheck(long timeout) {
+ mTimeout = timeout;
+ }
+
+ protected abstract boolean check();
+
+ public void run() {
+ if (check()) {
+ return;
+ }
+
+ long timeout = mTimeout;
+ while (timeout > 0) {
+ try {
+ Thread.sleep(TIME_SLICE);
+ } catch (InterruptedException e) {
+ Assert.fail("unexpected InterruptedException");
+ }
+
+ if (check()) {
+ return;
+ }
+
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail("unexpected timeout");
+ }
+
+ public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
+ throws Exception {
+ while (timeout > 0) {
+ if (condition.call()) {
+ return;
+ }
+
+ Thread.sleep(TIME_SLICE);
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail(message.toString());
+ }
+}
diff --git a/webkit/src/androidTest/java/androidx/webkit/ServiceWorkerClientCompatTest.java b/webkit/src/androidTest/java/androidx/webkit/ServiceWorkerClientCompatTest.java
new file mode 100644
index 0000000..aecbc34
--- /dev/null
+++ b/webkit/src/androidTest/java/androidx/webkit/ServiceWorkerClientCompatTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2018 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 androidx.webkit;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ServiceWorkerClientCompatTest {
+
+ // The BASE_URL does not matter since the tests will intercept the load, but it should be https
+ // for the Service Worker registration to succeed.
+ private static final String BASE_URL = "https://www.example.com/";
+ private static final String INDEX_URL = BASE_URL + "index.html";
+ private static final String SW_URL = BASE_URL + "sw.js";
+ private static final String FETCH_URL = BASE_URL + "fetch.html";
+
+ private static final String JS_INTERFACE_NAME = "Android";
+ private static final int POLLING_TIMEOUT = 10 * 1000;
+
+ // static HTML page always injected instead of the url loaded.
+ private static final String INDEX_RAW_HTML =
+ "<!DOCTYPE html>\n"
+ + "<html>\n"
+ + " <body>\n"
+ + " <script>\n"
+ + " navigator.serviceWorker.register('sw.js').then(function(reg) {\n"
+ + " " + JS_INTERFACE_NAME + ".registrationSuccess();\n"
+ + " }).catch(function(err) {\n"
+ + " console.error(err);\n"
+ + " });\n"
+ + " </script>\n"
+ + " </body>\n"
+ + "</html>\n";
+ private static final String SW_RAW_HTML = "fetch('fetch.html');";
+ private static final String SW_UNREGISTER_RAW_JS =
+ "navigator.serviceWorker.getRegistration().then(function(r) {"
+ + " r.unregister().then(function(success) {"
+ + " if (success) " + JS_INTERFACE_NAME + ".unregisterSuccess();"
+ + " else console.error('unregister() was not successful');"
+ + " });"
+ + "}).catch(function(err) {"
+ + " console.error(err);"
+ + "});";
+
+ private JavascriptStatusReceiver mJavascriptStatusReceiver;
+ private WebViewOnUiThread mOnUiThread;
+
+ // Both this test and WebViewOnUiThread need to override some of the methods on WebViewClient,
+ // so this test subclasses the WebViewClient from WebViewOnUiThread.
+ private static class InterceptClient extends WebViewOnUiThread.WaitForLoadedClient {
+
+ InterceptClient(WebViewOnUiThread webViewOnUiThread) throws Exception {
+ super(webViewOnUiThread);
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view,
+ WebResourceRequest request) {
+ // Only return content for INDEX_URL, deny all other requests.
+ try {
+ if (request.getUrl().toString().equals(INDEX_URL)) {
+ return new WebResourceResponse("text/html", "utf-8",
+ new ByteArrayInputStream(INDEX_RAW_HTML.getBytes("UTF-8")));
+ }
+ } catch (java.io.UnsupportedEncodingException e) { }
+ return new WebResourceResponse("text/html", "UTF-8", null);
+ }
+ }
+
+ public static class InterceptServiceWorkerClient extends ServiceWorkerClientCompat {
+ private List<WebResourceRequest> mInterceptedRequests = new ArrayList<WebResourceRequest>();
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+ // Records intercepted requests and only return content for SW_URL.
+ mInterceptedRequests.add(request);
+ try {
+ if (request.getUrl().toString().equals(SW_URL)) {
+ return new WebResourceResponse("application/javascript", "utf-8",
+ new ByteArrayInputStream(SW_RAW_HTML.getBytes("UTF-8")));
+ }
+ } catch (java.io.UnsupportedEncodingException e) { }
+ return new WebResourceResponse("text/html", "UTF-8", null);
+ }
+
+ List<WebResourceRequest> getInterceptedRequests() {
+ return mInterceptedRequests;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mOnUiThread = new WebViewOnUiThread();
+ mOnUiThread.getSettings().setJavaScriptEnabled(true);
+
+ mJavascriptStatusReceiver = new JavascriptStatusReceiver();
+ mOnUiThread.addJavascriptInterface(mJavascriptStatusReceiver, JS_INTERFACE_NAME);
+ mOnUiThread.setWebViewClient(new InterceptClient(mOnUiThread));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mOnUiThread != null) {
+ mOnUiThread.cleanUp();
+ }
+ }
+
+ // Test correct invocation of shouldInterceptRequest for Service Workers.
+ @Test
+ public void testServiceWorkerClientInterceptCallback() throws Exception {
+ // TODO(gsennton) activate this test for pre-N devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return;
+
+ final InterceptServiceWorkerClient mInterceptServiceWorkerClient =
+ new InterceptServiceWorkerClient();
+ ServiceWorkerControllerCompat swController = ServiceWorkerControllerCompat.getInstance();
+ swController.setServiceWorkerClient(mInterceptServiceWorkerClient);
+
+ mOnUiThread.loadUrlAndWaitForCompletion(INDEX_URL);
+
+ Callable<Boolean> registrationSuccess = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.mRegistrationSuccess;
+ }
+ };
+ PollingCheck.check("JS could not register Service Worker", POLLING_TIMEOUT,
+ registrationSuccess);
+
+ Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mInterceptServiceWorkerClient.getInterceptedRequests().size() >= 2;
+ }
+ };
+ PollingCheck.check("Service Worker intercept callbacks not invoked", POLLING_TIMEOUT,
+ receivedRequest);
+
+ List<WebResourceRequest> requests = mInterceptServiceWorkerClient.getInterceptedRequests();
+ assertEquals(2, requests.size());
+ assertEquals(SW_URL, requests.get(0).getUrl().toString());
+ assertEquals(FETCH_URL, requests.get(1).getUrl().toString());
+
+ // Clean-up, make sure to unregister the Service Worker.
+ mOnUiThread.evaluateJavascript(SW_UNREGISTER_RAW_JS, null);
+ Callable<Boolean> unregisterSuccess = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.mUnregisterSuccess;
+ }
+ };
+ PollingCheck.check("JS could not unregister Service Worker", POLLING_TIMEOUT,
+ unregisterSuccess);
+ }
+
+ // Object added to the page via AddJavascriptInterface() that is used by the test Javascript to
+ // notify back to Java if the Service Worker registration was successful.
+ public static final class JavascriptStatusReceiver {
+ public volatile boolean mRegistrationSuccess = false;
+ public volatile boolean mUnregisterSuccess = false;
+
+ @JavascriptInterface
+ public void registrationSuccess() {
+ mRegistrationSuccess = true;
+ }
+
+ @JavascriptInterface
+ public void unregisterSuccess() {
+ mUnregisterSuccess = true;
+ }
+ }
+}
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
index 3c64b9c..bd77fdb 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -28,6 +29,7 @@
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.webkit.SafeBrowsingResponse;
import android.webkit.ValueCallback;
@@ -83,6 +85,7 @@
assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
}
+ @Suppress // TODO(gsennton) remove @Suppress when b/76202025 has been resolved
@Test
public void testCheckThread() {
try {
@@ -235,4 +238,22 @@
Assert.fail("The privacy policy URL should be a well-formed URL");
}
}
+
+ /**
+ * WebViewCompat.getCurrentWebViewPackage should be null on pre-L devices.
+ * On L+ devices WebViewCompat.getCurrentWebViewPackage should be null only in exceptional
+ * circumstances - like when the WebView APK is being updated, or for Wear devices. The L+
+ * devices used in support library testing should have a non-null WebView package.
+ */
+ @Test
+ public void testGetCurrentWebViewPackage() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ assertNull(WebViewCompat.getCurrentWebViewPackage(
+ InstrumentationRegistry.getTargetContext()));
+ } else {
+ assertNotNull(
+ WebViewCompat.getCurrentWebViewPackage(
+ InstrumentationRegistry.getTargetContext()));
+ }
+ }
}
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
index 9b4c9e9..6e817c3 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
@@ -16,12 +16,42 @@
package androidx.webkit;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Bitmap;
+import android.os.Looper;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-public class WebViewOnUiThread {
+import java.util.concurrent.Callable;
+
+class WebViewOnUiThread {
+ /**
+ * The maximum time, in milliseconds (10 seconds) to wait for a load
+ * to be triggered.
+ */
+ private static final long LOAD_TIMEOUT = 10000;
+
+ /**
+ * Set to true after onPageFinished is called.
+ */
+ private boolean mLoaded;
+
+ /**
+ * The progress, in percentage, of the page load. Valid values are between
+ * 0 and 100.
+ */
+ private int mProgress;
+
+ /**
+ * The WebView that calls will be made on.
+ */
private WebView mWebView;
public WebViewOnUiThread() {
@@ -29,6 +59,8 @@
@Override
public void run() {
mWebView = new WebView(InstrumentationRegistry.getTargetContext());
+ mWebView.setWebViewClient(new WaitForLoadedClient(WebViewOnUiThread.this));
+ mWebView.setWebChromeClient(new WaitForProgressClient(WebViewOnUiThread.this));
}
});
}
@@ -70,10 +102,208 @@
});
}
- public WebView getWebViewOnCurrentThread() {
+ public void addJavascriptInterface(final Object object, final String name) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.addJavascriptInterface(object, name);
+ }
+ });
+ }
+
+ /**
+ * Called after a test is complete and the WebView should be disengaged from
+ * the tests.
+ */
+ public void cleanUp() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.clearHistory();
+ mWebView.clearCache(true);
+ mWebView.setWebChromeClient(null);
+ mWebView.setWebViewClient(null);
+ mWebView.destroy();
+ }
+ });
+ }
+
+ WebView getWebViewOnCurrentThread() {
return mWebView;
}
+ /**
+ * Called from WaitForLoadedClient.
+ */
+ synchronized void onPageStarted() {}
+
+ /**
+ * Called from WaitForLoadedClient, this is used to indicate that
+ * the page is loaded, but not drawn yet.
+ */
+ synchronized void onPageFinished() {
+ mLoaded = true;
+ this.notifyAll();
+ }
+
+ /**
+ * Called from the WebChrome client, this sets the current progress
+ * for a page.
+ * @param progress The progress made so far between 0 and 100.
+ */
+ synchronized void onProgressChanged(int progress) {
+ mProgress = progress;
+ this.notifyAll();
+ }
+
+ /**
+ * Calls loadUrl on the WebView and then waits onPageFinished,
+ * onNewPicture and onProgressChange to reach 100.
+ * Test fails if the load timeout elapses.
+ * @param url The URL to load.
+ */
+ void loadUrlAndWaitForCompletion(final String url) {
+ callAndWait(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.loadUrl(url);
+ }
+ });
+ }
+
+ /**
+ * Use this only when JavaScript causes a page load to wait for the
+ * page load to complete. Otherwise use loadUrlAndWaitForCompletion or
+ * similar functions.
+ */
+ void waitForLoadCompletion() {
+ waitForCriteria(LOAD_TIMEOUT,
+ new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return isLoaded();
+ }
+ });
+ clearLoad();
+ }
+
+ private void waitForCriteria(long timeout, Callable<Boolean> doneCriteria) {
+ if (isUiThread()) {
+ waitOnUiThread(timeout, doneCriteria);
+ } else {
+ waitOnTestThread(timeout, doneCriteria);
+ }
+ }
+
+ void evaluateJavascript(final String script, final ValueCallback<String> result) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.evaluateJavascript(script, result);
+ }
+ });
+ }
+
+ /**
+ * Returns true if the current thread is the UI thread based on the
+ * Looper.
+ */
+ private static boolean isUiThread() {
+ return (Looper.myLooper() == Looper.getMainLooper());
+ }
+
+ /**
+ * @return Whether or not the load has finished.
+ */
+ private synchronized boolean isLoaded() {
+ return mLoaded && mProgress == 100;
+ }
+
+ /**
+ * Makes a WebView call, waits for completion and then resets the
+ * load state in preparation for the next load call.
+ * @param call The call to make on the UI thread prior to waiting.
+ */
+ private void callAndWait(Runnable call) {
+ assertTrue("WebViewOnUiThread.load*AndWaitForCompletion calls "
+ + "may not be mixed with load* calls directly on WebView "
+ + "without calling waitForLoadCompletion after the load",
+ !isLoaded());
+ clearLoad(); // clear any extraneous signals from a previous load.
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(call);
+ waitForLoadCompletion();
+ }
+
+ /**
+ * Called whenever a load has been completed so that a subsequent call to
+ * waitForLoadCompletion doesn't return immediately.
+ */
+ private synchronized void clearLoad() {
+ mLoaded = false;
+ mProgress = 0;
+ }
+
+ /**
+ * Uses a polling mechanism, while pumping messages to check when the
+ * criteria is met.
+ */
+ private void waitOnUiThread(long timeout, final Callable<Boolean> doneCriteria) {
+ new PollingCheck(timeout) {
+ @Override
+ protected boolean check() {
+ pumpMessages();
+ try {
+ return doneCriteria.call();
+ } catch (Exception e) {
+ fail("Unexpected error while checking the criteria: " + e.getMessage());
+ return true;
+ }
+ }
+ }.run();
+ }
+
+ /**
+ * Uses a wait/notify to check when the criteria is met.
+ */
+ private synchronized void waitOnTestThread(long timeout, Callable<Boolean> doneCriteria) {
+ try {
+ long waitEnd = SystemClock.uptimeMillis() + timeout;
+ long timeRemaining = timeout;
+ while (!doneCriteria.call() && timeRemaining > 0) {
+ this.wait(timeRemaining);
+ timeRemaining = waitEnd - SystemClock.uptimeMillis();
+ }
+ assertTrue("Action failed to complete before timeout", doneCriteria.call());
+ } catch (InterruptedException e) {
+ // We'll just drop out of the loop and fail
+ } catch (Exception e) {
+ fail("Unexpected error while checking the criteria: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Pumps all currently-queued messages in the UI thread and then exits.
+ * This is useful to force processing while running tests in the UI thread.
+ */
+ private void pumpMessages() {
+ class ExitLoopException extends RuntimeException {
+ }
+
+ // Force loop to exit when processing this. Loop.quit() doesn't
+ // work because this is the main Loop.
+ mWebView.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ throw new ExitLoopException(); // exit loop!
+ }
+ });
+ try {
+ // Pump messages until our message gets through.
+ Looper.loop();
+ } catch (ExitLoopException e) {
+ }
+ }
+
private <T> T getValue(ValueGetter<T> getter) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(getter);
return getter.getValue();
@@ -93,4 +323,51 @@
return mValue;
}
}
+
+ /**
+ * A WebChromeClient used to capture the onProgressChanged for use
+ * in waitFor functions. If a test must override the WebChromeClient,
+ * it can derive from this class or call onProgressChanged
+ * directly.
+ */
+ public static class WaitForProgressClient extends WebChromeClient {
+ private WebViewOnUiThread mOnUiThread;
+
+ WaitForProgressClient(WebViewOnUiThread onUiThread) {
+ mOnUiThread = onUiThread;
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ super.onProgressChanged(view, newProgress);
+ mOnUiThread.onProgressChanged(newProgress);
+ }
+ }
+
+ /**
+ * A WebViewClient that captures the onPageFinished for use in
+ * waitFor functions. Using initializeWebView sets the WaitForLoadedClient
+ * into the WebView. If a test needs to set a specific WebViewClient and
+ * needs the waitForCompletion capability then it should derive from
+ * WaitForLoadedClient or call WebViewOnUiThread.onPageFinished.
+ */
+ public static class WaitForLoadedClient extends WebViewClient {
+ private WebViewOnUiThread mOnUiThread;
+
+ WaitForLoadedClient(WebViewOnUiThread onUiThread) {
+ mOnUiThread = onUiThread;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ mOnUiThread.onPageFinished();
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ mOnUiThread.onPageStarted();
+ }
+ }
}
diff --git a/webkit/src/main/java/androidx/webkit/ServiceWorkerClientCompat.java b/webkit/src/main/java/androidx/webkit/ServiceWorkerClientCompat.java
new file mode 100644
index 0000000..19aab8c
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/ServiceWorkerClientCompat.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 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 androidx.webkit;
+
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Base class for clients to capture Service Worker related callbacks,
+ * see {@link ServiceWorkerControllerCompat} for usage example.
+ */
+public abstract class ServiceWorkerClientCompat {
+ /**
+ *
+ * Notify the host application of a resource request and allow the
+ * application to return the data. If the return value is {@code null}, the
+ * Service Worker will continue to load the resource as usual.
+ * Otherwise, the return response and data will be used.
+ *
+ * <p class="note"><b>Note:</b> This method is called on a thread other than the UI thread so
+ * clients should exercise caution when accessing private data or the view system.
+ *
+ * @param request Object containing the details of the request.
+ * @return A {@link android.webkit.WebResourceResponse} containing the
+ * response information or {@code null} if the WebView should load the
+ * resource itself.
+ * @see android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView,
+ * WebResourceRequest)
+ *
+ */
+ public abstract WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request);
+}
diff --git a/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java b/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java
new file mode 100644
index 0000000..3eb55d2
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 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 androidx.webkit;
+
+import android.os.Build;
+import android.webkit.ServiceWorkerController;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.webkit.internal.FrameworkServiceWorkerController;
+import androidx.webkit.internal.ServiceWorkerControllerAdapter;
+import androidx.webkit.internal.WebViewGlueCommunicator;
+
+// TODO(gsennton) guard APIs with isFeatureSupported(String)
+
+/**
+ * Manages Service Workers used by WebView.
+ *
+ * <p>Example usage:
+ * <pre class="prettyprint">
+ * ServiceWorkerControllerCompat swController = ServiceWorkerControllerCompat.getInstance();
+ * swController.setServiceWorkerClient(new ServiceWorkerClientCompat() {
+ * {@literal @}Override
+ * public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+ * // Capture request here and generate response or allow pass-through
+ * // by returning null.
+ * return null;
+ * }
+ * });
+ * </pre>
+ */
+public abstract class ServiceWorkerControllerCompat {
+ /**
+ *
+ * @hide Don't allow apps to sub-class this class.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public ServiceWorkerControllerCompat() {}
+
+ /**
+ * Returns the default ServiceWorkerController instance. At present there is
+ * only one ServiceWorkerController instance for all WebView instances,
+ * however this restriction may be relaxed in the future.
+ *
+ * @return the default ServiceWorkerController instance
+ */
+ @NonNull
+ public static ServiceWorkerControllerCompat getInstance() {
+ return LAZY_HOLDER.INSTANCE;
+ }
+
+ private static class LAZY_HOLDER {
+ static final ServiceWorkerControllerCompat INSTANCE =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+ ? getFrameworkControllerCompat() : getSupportLibraryControllerCompat();
+ }
+
+ /**
+ * Return a version of {@link ServiceWorkerControllerCompat} that only uses framework APIs.
+ */
+ @RequiresApi(Build.VERSION_CODES.N)
+ private static ServiceWorkerControllerCompat getFrameworkControllerCompat() {
+ return new FrameworkServiceWorkerController(
+ ServiceWorkerController.getInstance());
+ }
+
+ private static ServiceWorkerControllerCompat getSupportLibraryControllerCompat() {
+ return new ServiceWorkerControllerAdapter(
+ WebViewGlueCommunicator.getFactory().getServiceWorkerController());
+ }
+
+ /**
+ *
+ * Gets the settings for all service workers.
+ *
+ * @return the current {@link ServiceWorkerWebSettingsCompat}
+ *
+ */
+ @NonNull
+ public abstract ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+
+ /**
+ *
+ * Sets the client to capture service worker related callbacks.
+ *
+ * A {@link ServiceWorkerClientCompat} should be set before any service workers are
+ * active, e.g. a safe place is before any WebView instances are created or
+ * pages loaded.
+ *
+ */
+ public abstract void setServiceWorkerClient(@Nullable ServiceWorkerClientCompat client);
+}
diff --git a/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java
new file mode 100644
index 0000000..61c46c3
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 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 androidx.webkit;
+
+import android.webkit.WebSettings;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Manages settings state for all Service Workers. These settings are not tied to
+ * the lifetime of any WebView because service workers can outlive WebView instances.
+ * The settings are similar to {@link WebSettings} but only settings relevant to
+ * Service Workers are supported.
+ */
+public abstract class ServiceWorkerWebSettingsCompat {
+ /**
+ * @hide Don't allow apps to sub-class this class.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public ServiceWorkerWebSettingsCompat() {}
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @IntDef(value = {
+ WebSettings.LOAD_DEFAULT,
+ WebSettings.LOAD_CACHE_ELSE_NETWORK,
+ WebSettings.LOAD_NO_CACHE,
+ WebSettings.LOAD_CACHE_ONLY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CacheMode {}
+
+ /**
+ *
+ * Overrides the way the cache is used, see {@link WebSettings#setCacheMode}.
+ *
+ * @param mode the mode to use. One of {@link WebSettings#LOAD_DEFAULT},
+ * {@link WebSettings#LOAD_CACHE_ELSE_NETWORK}, {@link WebSettings#LOAD_NO_CACHE}
+ * or {@link WebSettings#LOAD_CACHE_ONLY}. The default value is
+ * {@link WebSettings#LOAD_DEFAULT}.
+ *
+ */
+ public abstract void setCacheMode(@CacheMode int mode);
+
+ /**
+ *
+ * Gets the current setting for overriding the cache mode.
+ *
+ * @return the current setting for overriding the cache mode
+ * @see #setCacheMode
+ *
+ */
+ public abstract @CacheMode int getCacheMode();
+
+ /**
+ *
+ * Enables or disables content URL access from Service Workers, see
+ * {@link WebSettings#setAllowContentAccess}.
+ *
+ */
+ public abstract void setAllowContentAccess(boolean allow);
+
+ /**
+ *
+ * Gets whether Service Workers support content URL access.
+ *
+ * @see #setAllowContentAccess
+ *
+ */
+ public abstract boolean getAllowContentAccess();
+
+ /**
+ *
+ * Enables or disables file access within Service Workers, see
+ * {@link WebSettings#setAllowFileAccess}.
+ *
+ */
+ public abstract void setAllowFileAccess(boolean allow);
+
+ /**
+ *
+ * Gets whether Service Workers support file access.
+ *
+ * @see #setAllowFileAccess
+ *
+ */
+ public abstract boolean getAllowFileAccess();
+
+ /**
+ *
+ * Sets whether Service Workers should not load resources from the network,
+ * see {@link WebSettings#setBlockNetworkLoads}.
+ *
+ * @param flag {@code true} means block network loads by the Service Workers
+ *
+ */
+ public abstract void setBlockNetworkLoads(boolean flag);
+
+ /**
+ *
+ * Gets whether Service Workers are prohibited from loading any resources from the network.
+ *
+ * @return {@code true} if the Service Workers are not allowed to load any resources from the
+ * network
+ * @see #setBlockNetworkLoads
+ *
+ */
+ public abstract boolean getBlockNetworkLoads();
+}
diff --git a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index c73cda6..12d7e63 100644
--- a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -19,6 +19,13 @@
import android.os.Build;
import android.webkit.WebSettings;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
import androidx.webkit.internal.WebSettingsAdapter;
import androidx.webkit.internal.WebViewGlueCommunicator;
@@ -102,10 +109,25 @@
}
/**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @IntDef(flag = true, value = {
+ WebSettings.MENU_ITEM_NONE,
+ WebSettings.MENU_ITEM_SHARE,
+ WebSettings.MENU_ITEM_WEB_SEARCH,
+ WebSettings.MENU_ITEM_PROCESS_TEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.PARAMETER, ElementType.METHOD})
+ public @interface MenuItemFlags {}
+
+ /**
* Disables the action mode menu items according to {@code menuItems} flag.
* @param menuItems an integer field flag for the menu items to be disabled.
*/
- public static void setDisabledActionModeMenuItems(WebSettings webSettings, int menuItems) {
+ public static void setDisabledActionModeMenuItems(WebSettings webSettings,
+ @MenuItemFlags int menuItems) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
webSettings.setDisabledActionModeMenuItems(menuItems);
} else {
@@ -119,7 +141,7 @@
*
* @return all the disabled menu item flags combined with bitwise OR.
*/
- public static int getDisabledActionModeMenuItems(WebSettings webSettings) {
+ public static @MenuItemFlags int getDisabledActionModeMenuItems(WebSettings webSettings) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return webSettings.getDisabledActionModeMenuItems();
} else {
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 6a48e35..45def2e 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -17,6 +17,8 @@
package androidx.webkit;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
@@ -25,7 +27,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresFeature;
import androidx.core.os.BuildCompat;
+import androidx.webkit.internal.WebViewFeatureInternal;
import androidx.webkit.internal.WebViewGlueCommunicator;
import androidx.webkit.internal.WebViewProviderAdapter;
import androidx.webkit.internal.WebViewProviderFactoryAdapter;
@@ -104,13 +108,22 @@
* {@link android.webkit.WebSettings#setOffscreenPreRaster} for more details and do consider its
* caveats.
*
+ * This method should only be called if
+ * {@link WebViewFeature#isFeatureSupported(String)}
+ * returns true for {@link WebViewFeature#VISUAL_STATE_CALLBACK}.
+ *
* @param requestId An id that will be returned in the callback to allow callers to match
* requests with callbacks.
* @param callback The callback to be invoked.
*/
+ @SuppressWarnings("NewApi")
+ @RequiresFeature(name = WebViewFeature.VISUAL_STATE_CALLBACK,
+ enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
public static void postVisualStateCallback(@NonNull WebView webview, long requestId,
@NonNull final VisualStateCallback callback) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ WebViewFeatureInternal webViewFeature =
+ WebViewFeatureInternal.getFeature(WebViewFeature.VISUAL_STATE_CALLBACK);
+ if (webViewFeature.isSupportedByFramework()) {
webview.postVisualStateCallback(requestId,
new android.webkit.WebView.VisualStateCallback() {
@Override
@@ -118,10 +131,11 @@
callback.onComplete(l);
}
});
- } else {
- // TODO(gsennton): guard with if WebViewApk.hasFeature(POSTVISUALSTATECALLBACK)
+ } else if (webViewFeature.isSupportedByWebView()) {
checkThread(webview);
getProvider(webview).insertVisualStateCallback(requestId, callback);
+ } else {
+ WebViewFeatureInternal.throwUnsupportedOperationException("postVisualStateCallback");
}
}
@@ -198,6 +212,92 @@
}
}
+ /**
+ * If WebView has already been loaded into the current process this method will return the
+ * package that was used to load it. Otherwise, the package that would be used if the WebView
+ * was loaded right now will be returned; this does not cause WebView to be loaded, so this
+ * information may become outdated at any time.
+ * The WebView package changes either when the current WebView package is updated, disabled, or
+ * uninstalled. It can also be changed through a Developer Setting.
+ * If the WebView package changes, any app process that has loaded WebView will be killed. The
+ * next time the app starts and loads WebView it will use the new WebView package instead.
+ * @return the current WebView package, or {@code null} if there is none.
+ */
+ // Note that this API is not protected by a {@link androidx.webkit.WebViewFeature} since
+ // this feature is not dependent on the WebView APK.
+ @Nullable
+ public static PackageInfo getCurrentWebViewPackage(@NonNull Context context) {
+ // There was no WebView Package before Lollipop, the WebView code was part of the framework
+ // back then.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return null;
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ return WebView.getCurrentWebViewPackage();
+ } else { // L-N
+ try {
+ PackageInfo loadedWebViewPackageInfo = getLoadedWebViewPackageInfo();
+ if (loadedWebViewPackageInfo != null) return loadedWebViewPackageInfo;
+ } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException
+ | NoSuchMethodException e) {
+ return null;
+ }
+
+ // If WebViewFactory.getLoadedPackageInfo() returns null then WebView hasn't been loaded
+ // yet, in that case we need to fetch the name of the WebView package, and fetch the
+ // corresponding PackageInfo through the PackageManager
+ return getNotYetLoadedWebViewPackageInfo(context);
+ }
+ }
+
+ /**
+ * Return the PackageInfo of the currently loaded WebView APK. This method uses reflection and
+ * propagates any exceptions thrown, to the caller.
+ */
+ private static PackageInfo getLoadedWebViewPackageInfo()
+ throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
+ IllegalAccessException {
+ Class webViewFactoryClass = Class.forName("android.webkit.WebViewFactory");
+ PackageInfo webviewPackageInfo =
+ (PackageInfo) webViewFactoryClass.getMethod(
+ "getLoadedPackageInfo").invoke(null);
+ return webviewPackageInfo;
+ }
+
+ /**
+ * Return the PackageInfo of the WebView APK that would have been used as WebView implementation
+ * if WebView was to be loaded right now.
+ */
+ private static PackageInfo getNotYetLoadedWebViewPackageInfo(Context context) {
+ String webviewPackageName = null;
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
+ Class webViewFactoryClass = null;
+ webViewFactoryClass = Class.forName("android.webkit.WebViewFactory");
+
+ webviewPackageName = (String) webViewFactoryClass.getMethod(
+ "getWebViewPackageName").invoke(null);
+ } else {
+ Class webviewUpdateServiceClass =
+ Class.forName("android.webkit.WebViewUpdateService");
+ webviewPackageName = (String) webviewUpdateServiceClass.getMethod(
+ "getCurrentWebViewPackageName").invoke(null);
+ }
+ } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException
+ | NoSuchMethodException e) {
+ return null;
+ }
+ if (webviewPackageName == null) return null;
+ PackageManager pm = context.getPackageManager();
+ try {
+ return pm.getPackageInfo(webviewPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
private static WebViewProviderAdapter getProvider(WebView webview) {
return new WebViewProviderAdapter(createProvider(webview));
}
diff --git a/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
new file mode 100644
index 0000000..d514477
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 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 androidx.webkit;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StringDef;
+import androidx.webkit.internal.WebViewFeatureInternal;
+
+import org.chromium.support_lib_boundary.util.Features;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Utility class for checking which WebView Support Library features are supported on the device.
+ */
+public class WebViewFeature {
+
+ private WebViewFeature() {}
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @StringDef(value = {
+ VISUAL_STATE_CALLBACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.PARAMETER, ElementType.METHOD})
+ public @interface WebViewSupportFeature {}
+
+ /**
+ * Feature for {@link #isFeatureSupported(String)}.
+ * This feature covers
+ * {@link androidx.webkit.WebViewCompat#postVisualStateCallback(android.webkit.WebView, long,
+ * WebViewCompat.VisualStateCallback)}.
+ */
+ public static final String VISUAL_STATE_CALLBACK = Features.VISUAL_STATE_CALLBACK;
+
+ /**
+ * Return whether a feature is supported at run-time. This depends on the Android version of the
+ * device and the WebView APK on the device.
+ */
+ public static boolean isFeatureSupported(@NonNull @WebViewSupportFeature String feature) {
+ WebViewFeatureInternal webviewFeature = WebViewFeatureInternal.getFeature(feature);
+ return webviewFeature.isSupportedByFramework() || webviewFeature.isSupportedByWebView();
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerClient.java b/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerClient.java
new file mode 100644
index 0000000..c28346e
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerClient.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import android.os.Build;
+import android.webkit.ServiceWorkerClient;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+
+import androidx.annotation.RequiresApi;
+import androidx.webkit.ServiceWorkerClientCompat;
+
+/**
+ * A shim class that implements {@link ServiceWorkerClient} by delegating to a
+ * {@link ServiceWorkerClientCompat}.
+ * This class is used on up-to-date devices to avoid using reflection to call into WebView APK code.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+public class FrameworkServiceWorkerClient extends ServiceWorkerClient {
+ private final ServiceWorkerClientCompat mImpl;
+
+ public FrameworkServiceWorkerClient(ServiceWorkerClientCompat impl) {
+ mImpl = impl;
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+ return mImpl.shouldInterceptRequest(request);
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerController.java b/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerController.java
new file mode 100644
index 0000000..2e02777
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/FrameworkServiceWorkerController.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import android.os.Build;
+import android.webkit.ServiceWorkerController;
+
+import androidx.annotation.RequiresApi;
+import androidx.webkit.ServiceWorkerClientCompat;
+import androidx.webkit.ServiceWorkerControllerCompat;
+import androidx.webkit.ServiceWorkerWebSettingsCompat;
+
+/**
+ * Implementation of {@link ServiceWorkerControllerCompat} meant for use on up-to-date platforms.
+ * This class does not use reflection to bypass framework APIs - instead it uses android.webkit
+ * APIs.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+public class FrameworkServiceWorkerController extends ServiceWorkerControllerCompat {
+ private final ServiceWorkerController mImpl;
+ private ServiceWorkerWebSettingsCompat mSettings;
+
+ public FrameworkServiceWorkerController(ServiceWorkerController impl) {
+ mImpl = impl;
+ }
+
+ @Override
+ public ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings() {
+ if (mSettings == null) {
+ mSettings = new FrameworksServiceWorkerWebSettings(mImpl.getServiceWorkerWebSettings());
+ }
+ return mSettings;
+ }
+
+ @Override
+ public void setServiceWorkerClient(ServiceWorkerClientCompat client) {
+ mImpl.setServiceWorkerClient(new FrameworkServiceWorkerClient(client));
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/FrameworksServiceWorkerWebSettings.java b/webkit/src/main/java/androidx/webkit/internal/FrameworksServiceWorkerWebSettings.java
new file mode 100644
index 0000000..4373756
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/FrameworksServiceWorkerWebSettings.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import android.os.Build;
+import android.webkit.ServiceWorkerWebSettings;
+
+import androidx.annotation.RequiresApi;
+import androidx.webkit.ServiceWorkerWebSettingsCompat;
+
+/**
+ * Implementation of {@link ServiceWorkerWebSettingsCompat} meant for use on up-to-date platforms.
+ * This class does not use reflection to bypass framework APIs - instead it uses android.webkit
+ * APIs.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+public class FrameworksServiceWorkerWebSettings extends ServiceWorkerWebSettingsCompat {
+ private final ServiceWorkerWebSettings mImpl;
+
+ public FrameworksServiceWorkerWebSettings(ServiceWorkerWebSettings impl) {
+ mImpl = impl;
+ }
+
+ @Override
+ public void setCacheMode(int mode) {
+ mImpl.setCacheMode(mode);
+ }
+
+ @Override
+ public int getCacheMode() {
+ return mImpl.getCacheMode();
+ }
+
+ @Override
+ public void setAllowContentAccess(boolean allow) {
+ mImpl.setAllowContentAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowContentAccess() {
+ return mImpl.getAllowContentAccess();
+ }
+
+ @Override
+ public void setAllowFileAccess(boolean allow) {
+ mImpl.setAllowContentAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowFileAccess() {
+ return mImpl.getAllowFileAccess();
+ }
+
+ @Override
+ public void setBlockNetworkLoads(boolean flag) {
+ mImpl.setAllowContentAccess(flag);
+ }
+
+ @Override
+ public boolean getBlockNetworkLoads() {
+ return mImpl.getBlockNetworkLoads();
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerClientAdapter.java b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerClientAdapter.java
new file mode 100644
index 0000000..3ffeb83
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerClientAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+
+import androidx.webkit.ServiceWorkerClientCompat;
+
+import org.chromium.support_lib_boundary.ServiceWorkerClientBoundaryInterface;
+
+/**
+ * Adapter between {@link ServiceWorkerClientCompat} and
+ * {@link ServiceWorkerClientBoundaryInterface} (the corresponding interface shared with the support
+ * library glue in the WebView APK).
+ */
+public class ServiceWorkerClientAdapter implements ServiceWorkerClientBoundaryInterface {
+ private final ServiceWorkerClientCompat mClient;
+
+ public ServiceWorkerClientAdapter(ServiceWorkerClientCompat client) {
+ mClient = client;
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+ return mClient.shouldInterceptRequest(request);
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerControllerAdapter.java b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerControllerAdapter.java
new file mode 100644
index 0000000..4baa3ea
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerControllerAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import androidx.webkit.ServiceWorkerClientCompat;
+import androidx.webkit.ServiceWorkerControllerCompat;
+import androidx.webkit.ServiceWorkerWebSettingsCompat;
+
+import org.chromium.support_lib_boundary.ServiceWorkerControllerBoundaryInterface;
+import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface;
+import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
+
+/**
+ * Adapter between {@link ServiceWorkerControllerCompat} and
+ * {@link ServiceWorkerControllerBoundaryInterface} (the corresponding interface shared with the
+ * support library glue in the WebView APK).
+ */
+public class ServiceWorkerControllerAdapter extends ServiceWorkerControllerCompat {
+ private final ServiceWorkerControllerBoundaryInterface mImpl;
+ private final ServiceWorkerWebSettingsCompat mWebSettings;
+
+ public ServiceWorkerControllerAdapter(ServiceWorkerControllerBoundaryInterface impl) {
+ mImpl = impl;
+ mWebSettings = new ServiceWorkerWebSettingsAdapter(
+ BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+ ServiceWorkerWebSettingsBoundaryInterface.class,
+ mImpl.getServiceWorkerWebSettings()));
+ }
+
+ @Override
+ public ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings() {
+ return mWebSettings;
+ }
+
+ @Override
+ public void setServiceWorkerClient(ServiceWorkerClientCompat client) {
+ mImpl.setServiceWorkerClient(BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
+ new ServiceWorkerClientAdapter(client)));
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerWebSettingsAdapter.java b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerWebSettingsAdapter.java
new file mode 100644
index 0000000..fd49396
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/ServiceWorkerWebSettingsAdapter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import androidx.webkit.ServiceWorkerWebSettingsCompat;
+
+import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface;
+
+/**
+ * Adapter between {@link ServiceWorkerWebSettingsCompat} and
+ * {@link ServiceWorkerWebSettingsBoundaryInterface} (the corresponding interface shared with the
+ * support library glue in the WebView APK).
+ */
+public class ServiceWorkerWebSettingsAdapter extends ServiceWorkerWebSettingsCompat {
+ private final ServiceWorkerWebSettingsBoundaryInterface mImpl;
+
+ public ServiceWorkerWebSettingsAdapter(ServiceWorkerWebSettingsBoundaryInterface impl) {
+ mImpl = impl;
+ }
+
+ @Override
+ public void setCacheMode(int mode) {
+ mImpl.setCacheMode(mode);
+ }
+
+ @Override
+ public int getCacheMode() {
+ return mImpl.getCacheMode();
+ }
+
+ @Override
+ public void setAllowContentAccess(boolean allow) {
+ mImpl.setAllowContentAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowContentAccess() {
+ return mImpl.getAllowContentAccess();
+ }
+
+ @Override
+ public void setAllowFileAccess(boolean allow) {
+ mImpl.setAllowFileAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowFileAccess() {
+ return mImpl.getAllowFileAccess();
+ }
+
+ @Override
+ public void setBlockNetworkLoads(boolean flag) {
+ mImpl.setBlockNetworkLoads(flag);
+ }
+
+ @Override
+ public boolean getBlockNetworkLoads() {
+ return mImpl.getBlockNetworkLoads();
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/SupportLibraryInfo.java b/webkit/src/main/java/androidx/webkit/internal/SupportLibraryInfo.java
new file mode 100644
index 0000000..da8a02c
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/SupportLibraryInfo.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import org.chromium.support_lib_boundary.SupportLibraryInfoBoundaryInterface;
+import org.chromium.support_lib_boundary.util.Features;
+
+/**
+ * Contains information about the Android Support Library part of the WebView Support Library - this
+ * information is passed to the WebView APK code with the first WebView Support Library call.
+ */
+public class SupportLibraryInfo implements SupportLibraryInfoBoundaryInterface {
+ // Features supported by the support library itself (regardless of what the WebView APK
+ // supports).
+ private static final String[] SUPPORTED_FEATURES =
+ new String[] {
+ Features.VISUAL_STATE_CALLBACK
+ };
+
+ @Override
+ public String[] getSupportedFeatures() {
+ return SUPPORTED_FEATURES;
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
new file mode 100644
index 0000000..d16713c
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2018 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 androidx.webkit.internal;
+
+import android.os.Build;
+
+import androidx.webkit.WebViewFeature;
+import androidx.webkit.WebViewFeature.WebViewSupportFeature;
+
+/**
+ * Enum representing a WebView feature, this provides functionality for determining whether a
+ * feature is supported by the current framework and/or WebView APK.
+ */
+public enum WebViewFeatureInternal {
+ /**
+ * This feature covers
+ * {@link androidx.webkit.WebViewCompat#postVisualStateCallback(android.webkit.WebView, long,
+ * androidx.webkit.WebViewCompat.VisualStateCallback)}.
+ */
+ VISUAL_STATE_CALLBACK_FEATURE(WebViewFeature.VISUAL_STATE_CALLBACK, Build.VERSION_CODES.M);
+
+ private final String mFeatureValue;
+ private final int mOsVersion;
+
+ WebViewFeatureInternal(@WebViewSupportFeature String featureValue, int osVersion) {
+ mFeatureValue = featureValue;
+ mOsVersion = osVersion;
+ }
+
+ /**
+ * Return the {@link WebViewFeatureInternal} corresponding to {@param feature}.
+ */
+ public static WebViewFeatureInternal getFeature(@WebViewSupportFeature String feature) {
+ switch (feature) {
+ case WebViewFeature.VISUAL_STATE_CALLBACK:
+ return VISUAL_STATE_CALLBACK_FEATURE;
+ default:
+ throw new RuntimeException("Unknown feature " + feature);
+ }
+ }
+
+ /**
+ * Return whether this {@link WebViewFeature} is supported by the framework of the current
+ * device.
+ */
+ public boolean isSupportedByFramework() {
+ return Build.VERSION.SDK_INT >= mOsVersion;
+ }
+
+ /**
+ * Return whether this {@link WebViewFeature} is supported by the current WebView APK.
+ */
+ public boolean isSupportedByWebView() {
+ String[] webviewFeatures = LAZY_HOLDER.WEBVIEW_APK_FEATURES;
+ for (String webviewFeature : webviewFeatures) {
+ if (webviewFeature.equals(mFeatureValue)) return true;
+ }
+ return false;
+ }
+
+ private static class LAZY_HOLDER {
+ static final String[] WEBVIEW_APK_FEATURES =
+ WebViewGlueCommunicator.getFactory().getWebViewFeatures();
+ }
+
+ /**
+ * Utility method for throwing an exception explaining that the feature the app trying to use
+ * isn't supported.
+ */
+ public static void throwUnsupportedOperationException(String feature) {
+ throw new UnsupportedOperationException("Feature " + feature
+ + " is not supported by the current version of the framework and WebView APK");
+ }
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
index 1325ed0..22cc1f4 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
@@ -61,10 +61,14 @@
Class<?> glueFactoryProviderFetcherClass = Class.forName(
GLUE_FACTORY_PROVIDER_FETCHER_CLASS, false, getWebViewClassLoader());
Method createProviderFactoryMethod = glueFactoryProviderFetcherClass.getDeclaredMethod(
- GLUE_FACTORY_PROVIDER_FETCHER_METHOD);
- return (InvocationHandler) createProviderFactoryMethod.invoke(null);
+ GLUE_FACTORY_PROVIDER_FETCHER_METHOD, InvocationHandler.class);
+ return (InvocationHandler) createProviderFactoryMethod.invoke(null,
+ BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
+ new SupportLibraryInfo()));
} catch (IllegalAccessException | InvocationTargetException | ClassNotFoundException
| NoSuchMethodException e) {
+ // TODO(gsennton) if this happens we should avoid throwing an exception! And probably
+ // declare that the list of features supported by the WebView APK is empty.
throw new RuntimeException(e);
}
}
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
index 62ce41e..efe0e64 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
@@ -18,6 +18,7 @@
import android.webkit.WebView;
+import org.chromium.support_lib_boundary.ServiceWorkerControllerBoundaryInterface;
import org.chromium.support_lib_boundary.StaticsBoundaryInterface;
import org.chromium.support_lib_boundary.WebViewProviderBoundaryInterface;
import org.chromium.support_lib_boundary.WebViewProviderFactoryBoundaryInterface;
@@ -63,4 +64,20 @@
return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
StaticsBoundaryInterface.class, mImpl.getStatics());
}
+
+ /**
+ * Adapter method for fetching the features supported by the current WebView APK.
+ */
+ public String[] getWebViewFeatures() {
+ return mImpl.getSupportedFeatures();
+ }
+
+ /**
+ * Adapter method for fetching the support library class representing
+ * {@link android.webkit.ServiceWorkerController}.
+ */
+ public ServiceWorkerControllerBoundaryInterface getServiceWorkerController() {
+ return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+ ServiceWorkerControllerBoundaryInterface.class, mImpl.getServiceWorkerController());
+ }
}